# 时间: 2021-10-25
# 线程

#[[
会话是用来承载进程组的, 会话当中可以有一个或者多个进程组, 一个进程组中可以有一个或者多个进程,
一个进程中可以有一个或多个线程。

一个线程就是一个正在运行的函数。
线程没有主次之分, main线程, 其它线程们。

一个新的库发布出来, 默认就要支持多线程并发。
如果这个库不支持多线程并发, 开发者应需说明。

posix线程是一套标准, 而不是实现。

pthread_t是什么类型, 不知道。有可能pthread_t是整数类型, 有可能是一个结构体,
也有可能是一个共用体, 等... 因为posix线程是一套标准, 各家的实现可能不同。

不要使用 printf(%d, ...); 打印pthread_t类型, 因为不知道pthread_t是什么类型,
各家的实现可能各不相同。请注意！

在 linux下, pthread_t是一个整型数。

使用 ps asm 可以查看进程更多的信息。

例如:

 171591 ?        -      0:06 [kworker/1:2-events]
      - -        I      0:06 -
 171892 ?        -      0:00 /usr/libexec/fwupd/fwupd
      - -        Ssl    0:00 -
      - -        Ssl    0:00 -
      - -        Ssl    0:00 -
      - -        Ssl    0:00 -
      - -        Ssl    0:00 -
 172758 ?        -      0:01 [kworker/0:1-events]
      - -        I      0:01 -
 172831 ?        -      0:00 [kworker/1:1-events]
      - -        I      0:00 -
 172903 ?        -      0:00 sshd: liuyou [priv]
      - -        Ss     0:00 -
 172980 ?        -      0:01 sshd: liuyou@pts/3
      - -        R      0:01 -
 173001 ?        -      0:00 /usr/lib/openssh/sftp-server
      - -        Ss     0:00 -
 173051 ?        -      0:00 /usr/lib/openssh/sftp-server
      - -        Ss     0:00 -
 173617 ?        -      0:00 [kworker/u256:1-events_unbound]
      - -        I      0:00 -
 173623 ?        -      0:00 [kworker/0:0-cgroup_destroy]
      - -        I      0:00 -
 173624 ?        -      0:00 [kworker/u256:0-events_unbound]
      - -        I      0:00 -
 173629 pts/3    -      0:00 -bash
      - -        Ss     0:00 -
 173636 pts/3    -      0:00 ps axm
      - -        R+     0:00 -

可以观察到 每个进程下至少有一个 - - , 指的是一个进程空间里面至少是有一个线程的(main线程)。
每一个 - - 就是一个线程在跑。

ps ax -L

-L: 指的是以 linux的方式来查看当前进程和线程的关系。

例如:
   1945    1945 ?        Sl     0:00 /usr/libexec/gsd-printer
   1945    1946 ?        Sl     0:00 /usr/libexec/gsd-printer
   1945    1947 ?        Sl     0:00 /usr/libexec/gsd-printer
   1951    1951 ?        Sl     1:41 /snap/snap-store/547/usr/bin/snap-store --gapplication-service
   1951    2079 ?        Sl     0:29 /snap/snap-store/547/usr/bin/snap-store --gapplication-service
   1951    2080 ?        Sl     0:00 /snap/snap-store/547/usr/bin/snap-store --gapplication-service
   1951    2081 ?        Sl     0:01 /snap/snap-store/547/usr/bin/snap-store --gapplication-service
   1951    2095 ?        Sl     0:00 /snap/snap-store/547/usr/bin/snap-store --gapplication-service
   1951   29639 ?        Sl     0:00 /snap/snap-store/547/usr/bin/snap-store --gapplication-service
   1951   29640 ?        Sl     0:00 /snap/snap-store/547/usr/bin/snap-store --gapplication-service
   1951   29641 ?        Sl     0:00 /snap/snap-store/547/usr/bin/snap-store --gapplication-service
   1951   29642 ?        Sl     0:00 /snap/snap-store/547/usr/bin/snap-store --gapplication-service
   1951   29643 ?        Sl     0:00 /snap/snap-store/547/usr/bin/snap-store --gapplication-service
   1951   29644 ?        Sl     0:00 /snap/snap-store/547/usr/bin/snap-store --gapplication-service
   1951   29645 ?        Sl     0:00 /snap/snap-store/547/usr/bin/snap-store --gapplication-service
   1951   29646 ?        Sl     0:00 /snap/snap-store/547/usr/bin/snap-store --gapplication-service
   1951   29650 ?        Sl     0:00 /snap/snap-store/547/usr/bin/snap-store --gapplication-service
   1951   29655 ?        Sl     0:00 /snap/snap-store/547/usr/bin/snap-store --gapplication-service
   1956    1956 ?        Sl     0:03 /usr/libexec/ibus-engine-simple
   1956    1957 ?        Sl     0:00 /usr/libexec/ibus-engine-simple
   1956    1958 ?        Sl     0:09 /usr/libexec/ibus-engine-simple
   1974    1974 ?        Ssl    0:00 /usr/libexec/xdg-document-portal
   1974    1976 ?        Ssl    0:00 /usr/libexec/xdg-document-portal
   1974    1978 ?        Ssl    0:00 /usr/libexec/xdg-document-portal
   1974    1981 ?        Ssl    0:00 /usr/libexec/xdg-document-portal
   1974    1982 ?        Ssl    0:00 /usr/libexec/xdg-document-portal
   1974    1984 ?        Ssl    0:00 /usr/libexec/xdg-document-portal
   2082    2082 ?        Ssl    0:01 /usr/libexec/xdg-desktop-portal
   2082    2083 ?        Ssl    0:19 /usr/libexec/xdg-desktop-portal
   2082    2084 ?        Ssl    0:02 /usr/libexec/xdg-desktop-portal
   2082    2093 ?        Ssl    0:00 /usr/libexec/xdg-desktop-portal
   2082    2094 ?        Ssl    0:00 /usr/libexec/xdg-desktop-portal
   2086    2086 ?        Ssl    0:20 /usr/libexec/xdg-desktop-portal-gtk
   2086    2087 ?        Ssl    0:00 /usr/libexec/xdg-desktop-portal-gtk
   2086    2088 ?        Ssl    0:16 /usr/libexec/xdg-desktop-portal-gtk
   2086    2092 ?        Ssl    0:00 /usr/libexec/xdg-desktop-portal-gtk


PID LWP(light work process)
ps ax -L

信号处理函数应该越短越好, 里面尽量不要用IO, 不能用不可重入的函数, 等等,
你只能安全的在信号处理函数中用一些计算和系统调用, 其它都慎用。

而线程不存在这样的问题, 可以在线程中放心的使用IO函数, 用可重入或者不可重入的函数等等。

所以, 要做到线程的安全, 比做到信号的安全要容易一些。

因为线程是先标准化的, 然后事实的。而多进程包括信号, 是先称为事实, 然后再进行标准化的, 一定会有边边角角超出标准的内容,
没有办法, 把各家的实现完全标准化。

]]

#[[

pthread_t

pthread_equal();

PTHREAD_EQUAL(3)                                                                Linux Programmer's Manual                                                                PTHREAD_EQUAL(3)

NAME
       pthread_equal - compare thread IDs
       比较线程ID。

SYNOPSIS
       #include <pthread.h>

       // 比较 tid 不能使用 < > == 等来比较。
       // 必须使用这个 pthread_equal();这个函数。
       // 因为我们不知道 pthread_t 类型是什么类型。
       // 标准就规定, 各家实现一个函数用来比较 pthread_t 类型的对象/变量。

       int pthread_equal(pthread_t t1, pthread_t t2);

       Compile and link with -pthread.
       使用 -pthread 编译和链接。

DESCRIPTION
       The pthread_equal() function compares two thread identifiers.
       pthread_equal() 函数比较两个线程标识符。

RETURN VALUE
       If the two thread IDs are equal, pthread_equal() returns a nonzero value; otherwise, it returns 0.
       如果两个线程 ID 相等，则 pthread_equal() 返回一个非零值；否则返回 0。

ERRORS
       This function always succeeds.
       这个函数总是成功。

ATTRIBUTES
       For an explanation of the terms used in this section, see attributes(7).

       ┌────────────────┬───────────────┬─────────┐
       │Interface       │ Attribute     │ Value   │
       ├────────────────┼───────────────┼─────────┤
       │pthread_equal() │ Thread safety │ MT-Safe │
       └────────────────┴───────────────┴─────────┘
CONFORMING TO
       POSIX.1-2001, POSIX.1-2008.

NOTES
       The pthread_equal() function is necessary because thread IDs should be considered opaque: there is no portable way for applications to directly compare two pthread_t values.
       pthread_equal() 函数是必需的，因为线程 ID 应该被认为是不透明的：应用程序没有可移植的方式来直接比较两个 pthread_t 值。

SEE ALSO
       pthread_create(3), pthread_self(3), pthreads(7)

COLOPHON
       This  page  is  part  of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest version of this page, can be
       found at https://www.kernel.org/doc/man-pages/.

Linux                                                                                   2015-08-08                                                                       PTHREAD_EQUAL(3)


]]

link_libraries(pthread)

#[[

pthread_self();

PTHREAD_SELF(3)                                                                 Linux Programmer's Manual                                                                 PTHREAD_SELF(3)

NAME
       pthread_self - obtain ID of the calling thread
       获取调用线程的ID
       获取调用这个函数线程的 tid。

SYNOPSIS
       #include <pthread.h>

       // 相当于进程中的 getpid();函数。

       pthread_t pthread_self(void);

       Compile and link with -pthread.

DESCRIPTION
       The pthread_self() function returns the ID of the calling thread.  This is the same value that is returned in *thread in the pthread_create(3) call that created this thread.
       pthread_self() 函数返回调用线程的 ID。这与在创建此线程的 pthread_create(3) 调用中的线程中返回的值相同。

RETURN VALUE
       This function always succeeds, returning the calling thread's ID.
       此函数总是成功，返回调用线程的 ID。

ERRORS
       This function always succeeds.

ATTRIBUTES
       For an explanation of the terms used in this section, see attributes(7).

       ┌───────────────┬───────────────┬─────────┐
       │Interface      │ Attribute     │ Value   │
       ├───────────────┼───────────────┼─────────┤
       │pthread_self() │ Thread safety │ MT-Safe │
       └───────────────┴───────────────┴─────────┘
CONFORMING TO
       POSIX.1-2001, POSIX.1-2008.

NOTES
       POSIX.1  allows  an  implementation wide freedom in choosing the type used to represent a thread ID; for example, representation using either an arithmetic type or a structure is
       permitted.  Therefore, variables of type pthread_t can't portably be compared using the C equality operator (==); use pthread_equal(3) instead.
       POSIX.1 允许在选择用于表示线程 ID 的类型方面实现广泛的自由；例如，允许使用算术类型或结构来表示。
       因此，pthread_t 类型的变量不能使用 C 相等运算符 (==) 进行可移植的比较；请改用 pthread_equal(3)。

       Thread identifiers should be considered opaque: any attempt to use a thread ID other than in pthreads calls is nonportable and can lead to unspecified results.
       线程标识符应该被认为是不透明的：任何在 pthreads 调用之外使用线程 ID 的尝试都是不可移植的，并且可能导致未指定的结果。

       Thread IDs are guaranteed to be unique only within a process.  A thread ID may be reused after a terminated thread has been joined, or a detached thread has terminated.
       线程 ID 保证仅在进程内是唯一的。在加入已终止的线程或已终止分离的线程后，可以重新使用线程 ID。

       The thread ID returned by pthread_self() is not the same thing as the kernel thread ID returned by a call to gettid(2).
       pthread_self() 返回的线程 ID 与调用 gettid(2) 返回的内核线程 ID 不同。

SEE ALSO
       pthread_create(3), pthread_equal(3), pthreads(7)

COLOPHON
       This page is part of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest version of  this  page,  can  be
       found at https://www.kernel.org/doc/man-pages/.

Linux                                                                                   2017-09-15                                                                        PTHREAD_SELF(3)

]]

#[[
创建一个线程

pthread_create();

PTHREAD_CREATE(3)                                                               Linux Programmer's Manual                                                               PTHREAD_CREATE(3)

NAME
       pthread_create - create a new thread

SYNOPSIS
       #include <pthread.h>

       // 第一个参数要一个指针, 这个函数会回填这个指针指向的空间。
       // 用户需要创建一个 pthread_attr_t 的结构体, 然后把这个结构体的字段设置好,
       // 然后把地址传入这个函数, 指定这个线程的属性。
       // 如果传入 nullptr 的话, 不进行线程属性的指定, 使用默认的线程属性。
       // 第三个参数是一个函数指针, 创建出来的新线程通过调用这个函数开始执行。
       // 通过 arg 来为这个新的线程传入参数, 这个 arg 设计为 void*, 这是
       // 非常灵活的, 可以传任意类型的数据(整形, 数组, 结构体, 类等等), 如果没有数据传入, 那就传入 nullptr。
       // 返回类型也是 void*, 可以返回任意类型的数据, 如果没有数据返回, 那就返回 nullptr。

       int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);

       Compile and link with -pthread.

DESCRIPTION
       The  pthread_create()  function  starts  a  new thread in the calling process.  The new thread starts execution by invoking start_routine(); arg is passed as the sole argument of
       start_routine().
       pthread_create() 函数在调用进程中启动一个新线程。
       新线程通过调用 start_routine() 开始执行, arg 参数作为 start_routine() 的唯一参数传递。

       The new thread terminates in one of the following ways:
       新线程以下列方式之一终止:

       * It calls pthread_exit(3), specifying an exit status value that is available to another thread in the same process that calls pthread_join(3).
       * 它调用 pthread_exit(3)，指定一个退出状态值，该值可供调用 pthread_join(3) 的同一进程中的另一个线程使用。

       * It returns from start_routine().  This is equivalent to calling pthread_exit(3) with the value supplied in the return statement.
       * 它从 start_routine() 返回。这等效于使用 return 语句中提供的值调用 pthread_exit(3)。

       * It is canceled (see pthread_cancel(3)).
       * 它被取消（请参阅 pthread_cancel(3)）。

       * Any of the threads in the process calls exit(3), or the main thread performs a return from main().  This causes the termination of all threads in the process.
       * 进程中的任何线程调用exit(3)，或者主线程执行main() 的返回。这会导致进程中的所有线程终止。

       The attr argument points to a pthread_attr_t structure whose contents are used at thread creation time to determine attributes for the new thread; this structure  is  initialized
       using pthread_attr_init(3) and related functions.  If attr is NULL, then the thread is created with default attributes.
       attr 参数指向 pthread_attr_t 结构，其内容在线程创建时用于确定新线程的属性；该结构使用 pthread_attr_init(3) 和相关函数进行初始化。如果 attr 为 NULL，则使用默认属性创建线程。

       Before returning, a successful call to pthread_create() stores the ID of the new thread in the buffer pointed to by thread; this identifier is used to refer to the thread in sub‐
       sequent calls to other pthreads functions.
       在返回之前，成功调用 pthread_create() 会将新线程的 ID 存储在 thread 指向的缓冲区中；此标识符用于在后续调用其他 pthreads 函数时引用该线程。

       The new thread inherits a copy of the creating thread's signal mask (pthread_sigmask(3)).  The set of pending signals for the new thread is empty (sigpending(2)).  The new thread
       does not inherit the creating thread's alternate signal stack (sigaltstack(2)).
       新线程继承创建线程的信号掩码 (pthread_sigmask(3)) 的副本。新线程的挂起信号集为空（sigpending(2)）。新线程不继承创建线程的备用信号堆栈 (sigaltstack(2))。

       The new thread inherits the calling thread's floating-point environment (fenv(3)).
       新线程继承调用线程的浮点环境 (fenv(3))。

       The initial value of the new thread's CPU-time clock is 0 (see pthread_getcpuclockid(3)).
       新线程的 CPU 时间时钟的初始值为 0（请参阅 pthread_getcpuclockid(3)）。

   Linux-specific details
   特定于 Linux 的详细信息
       The new thread inherits copies of the calling thread's capability sets (see capabilities(7)) and CPU affinity mask (see sched_setaffinity(2)).
       新线程继承调用线程的能力集（请参阅capabilities(7)）和CPU 关联掩码（请参阅sched_setaffinity(2)）的副本。

RETURN VALUE
       On success, pthread_create() returns 0; on error, it returns an error number, and the contents of *thread are undefined.
       成功时，pthread_create() 返回 0；出错时，它返回一个errno，线程的内容是未定义的。

       成功时返回0, 出错了返回的是一个errno, 这跟之前是有区别的,
       之前都是如果出错会设置errno, 并且返回 -1。
       而这里是直接把errno给你了。

ERRORS
       EAGAIN Insufficient resources to create another thread.
       EAGAIN 资源不足，无法创建另一个线程。

       EAGAIN A  system-imposed  limit on the number of threads was encountered.  There are a number of limits that may trigger this error: the RLIMIT_NPROC soft resource limit (set via
              setrlimit(2)), which limits the number of processes and threads for a real user ID, was reached; the kernel's system-wide limit on the number  of  processes  and  threads,
              /proc/sys/kernel/threads-max, was reached (see proc(5)); or the maximum number of PIDs, /proc/sys/kernel/pid_max, was reached (see proc(5)).
       EAGAIN 遇到系统对线程数施加的限制。有许多限制可能会触发此错误：已达到 RLIMIT_NPROC 软资源限制（通过 setrlimit(2) 设置），该限制限制了真实用户 ID 的进程和线程数；已达到内核对进程和线程数的系统范围限制 procsyskernelthreads-max（请参阅 proc(5)）；或者已达到最大 PID 数 procsyskernelpid_max（请参阅 proc(5)）。

       EINVAL Invalid settings in attr.
       EINVAL attr 中的设置无效。

       EPERM  No permission to set the scheduling policy and parameters specified in attr.
       EPERM 无权设置attr中指定的调度策略和参数。

ATTRIBUTES
       For an explanation of the terms used in this section, see attributes(7).

       ┌─────────────────┬───────────────┬─────────┐
       │Interface        │ Attribute     │ Value   │
       ├─────────────────┼───────────────┼─────────┤
       │pthread_create() │ Thread safety │ MT-Safe │
       └─────────────────┴───────────────┴─────────┘

CONFORMING TO
       POSIX.1-2001, POSIX.1-2008.

NOTES
       See  pthread_self(3)  for  further information on the thread ID returned in *thread by pthread_create().  Unless real-time scheduling policies are being employed, after a call to
       pthread_create(), it is indeterminate which thread—the caller or the new thread—will next execute.
       有关 pthread_create() 在 thread 中返回的线程 ID 的更多信息，请参阅 pthread_self(3)。除非采用实时调度策略，否则在调用 pthread_create() 之后，下一次执行哪个线程（调用者或新线程）是不确定的。

       A thread may either be joinable or detached.  If a thread is joinable, then another thread can call pthread_join(3) to wait for the thread to terminate and fetch its exit status.
       Only  when a terminated joinable thread has been joined are the last of its resources released back to the system.  When a detached thread terminates, its resources are automati‐
       cally released back to the system: it is not possible to join with the thread in order to obtain its exit status.  Making a thread detached is useful for  some  types  of  daemon
       threads whose exit status the application does not need to care about.  By default, a new thread is created in a joinable state, unless attr was set to create the thread in a de‐
       tached state (using pthread_attr_setdetachstate(3)).
       一个线程可以是可连接的，也可以是分离的。如果一个线程是可加入的，那么另一个线程可以调用 pthread_join(3) 来等待线程终止并获取其退出状态。只有当一个终止的可连接线程被加入时，它的最后一个资源才会释放回系统。当一个分离的线程终止时，它的资源会自动释放回系统：不可能加入线程以获得它的退出状态。使线程分离对于某些类型的守护线程非常有用，它们的退出状态应用程序不需要关心。默认情况下，一个新线程在可连接状态下创建，除非 attr 设置为在分离状态下创建线程（使用 pthread_attr_setdetachstate(3)）。

       Under the NPTL threading implementation, if the RLIMIT_STACK soft resource limit at the time the program started has any value other than "unlimited", then it determines the  de‐
       fault stack size of new threads.  Using pthread_attr_setstacksize(3), the stack size attribute can be explicitly set in the attr argument used to create a thread, in order to ob‐
       tain a stack size other than the default.  If the RLIMIT_STACK resource limit is set to "unlimited", a per-architecture value is used for the stack size.  Here is the value for a
       few architectures:
       在 NPTL 线程实现下，如果程序启动时的 RLIMIT_STACK 软资源限制具有除“unlimited”以外的任何值，则它决定了新线程的默认堆栈大小。使用 pthread_attr_setstacksize(3)，可以在用于创建线程的 attr 参数中显式设置堆栈大小属性，以获得不同于默认值的堆栈大小。如果 RLIMIT_STACK 资源限制设置为“无限制”，则堆栈大小使用每个架构的值。以下是一些架构的价值:

              ┌─────────────┬────────────────────┐
              │Architecture │ Default stack size │
                (体系结构)      (默认的栈空间大小)
              ├─────────────┼────────────────────┤
              │i386         │               2 MB │
              ├─────────────┼────────────────────┤
              │IA-64        │              32 MB │
              ├─────────────┼────────────────────┤
              │PowerPC      │               4 MB │
              ├─────────────┼────────────────────┤
              │S/390        │               2 MB │
              ├─────────────┼────────────────────┤
              │Sparc-32     │               2 MB │
              ├─────────────┼────────────────────┤
              │Sparc-64     │               4 MB │
              ├─────────────┼────────────────────┤
              │x86_64       │               2 MB │
              └─────────────┴────────────────────┘
BUGS
       In  the  obsolete  LinuxThreads  implementation, each of the threads in a process has a different process ID.  This is in violation of the POSIX threads specification, and is the
       source of many other nonconformances to the standard; see pthreads(7).

SEE ALSO
       getrlimit(2), pthread_attr_init(3), pthread_cancel(3), pthread_detach(3), pthread_equal(3), pthread_exit(3), pthread_getattr_np(3), pthread_join(3), pthread_self(3),
       pthread_setattr_default_np(3), pthreads(7)

COLOPHON
       This page is part of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest version of this page, can be
       found at https://www.kernel.org/doc/man-pages/.

Linux                                                                                   2018-04-30                                                                      PTHREAD_CREATE(3)

]]

#[[
线程的调度取决于CPU的调度策略。
]]
add_executable(thread-createthread1 thread-createthread1.cpp)

#[[
线程的终止

    4种:
        1> 线程从启动例程中返回, 例如: return nullptr; 返回值就是线程的退出码。
        2> 线程可以被统一进程中的其它线程取消 pthread_cancel();
        3> 线程调用 pthread_exit(nullptr); 这和 return nullptr;效果相同。
        4> 进程中的任何线程调用exit(3)，或者 主线程执行 main() 的 return返回语句。这两种情况会导致进程中的所有线程终止。

    如果调用 pthread_exit();的这个线程是当前进程空间内最后一个正在运行的线程,
    那么当前进程也会结束。
    如果最后一个线程从启动例程返回, 也会导致进程结束。

]]

#[[
pthread_exit();

PTHREAD_EXIT(3)                                                                 Linux Programmer's Manual                                                                 PTHREAD_EXIT(3)

NAME
       pthread_exit - terminate calling thread
       终止调用线程。

SYNOPSIS
       #include <pthread.h>

       void pthread_exit(void *retval);

       Compile and link with -pthread.

DESCRIPTION
       The  pthread_exit() function terminates the calling thread and returns a value via retval that (if the thread is joinable) is available to another thread in the same process that
       calls pthread_join(3).
       pthread_exit() 函数终止调用线程并通过 retval 返回一个值（如果该线程可连接）可用于调用 pthread_join(3) 的同一进程中的另一个线程。

       Any clean-up handlers established by pthread_cleanup_push(3) that have not yet been popped, are popped (in the reverse of the order in which they were pushed) and  executed.   If
       the thread has any thread-specific data, then, after the clean-up handlers have been executed, the corresponding destructor functions are called, in an unspecified order.
       任何由 pthread_cleanup_push(3) 建立但尚未弹出的清理处理程序都将弹出（与推送它们的顺序相反）并执行。如果线程有任何线程特定的数据，则在执行清理处理程序后，会以未指定的顺序调用相应的析构函数。

       When  a  thread  terminates,  process-shared  resources  (e.g.,  mutexes,  condition variables, semaphores, and file descriptors) are not released, and functions registered using
       atexit(3) are not called.
       当线程终止时，进程共享资源（例如互斥锁、条件变量、信号量和文件描述符）不会被释放，并且不会调用使用 atexit(3) 注册的函数。

       After the last thread in a process terminates, the process terminates as by calling exit(3) with an exit status of zero; thus, process-shared resources are released and functions
       registered using atexit(3) are called.
       在进程中的最后一个线程终止后，该进程通过调用 exit(3) 以退出状态为零来终止；因此，进程共享资源被释放，使用 atexit(3) 注册的函数被调用。

RETURN VALUE
       This function does not return to the caller.
       此函数不会返回给调用者。

ERRORS
       This function always succeeds.
       此函数总是成功。

ATTRIBUTES
       For an explanation of the terms used in this section, see attributes(7).

       ┌───────────────┬───────────────┬─────────┐
       │Interface      │ Attribute     │ Value   │
       ├───────────────┼───────────────┼─────────┤
       │pthread_exit() │ Thread safety │ MT-Safe │
       └───────────────┴───────────────┴─────────┘
CONFORMING TO
       POSIX.1-2001, POSIX.1-2008.

NOTES
       Performing  a  return  from  the  start  function of any thread other than the main thread results in an implicit call to pthread_exit(), using the function's return value as the
       thread's exit status.
       从主线程以外的任何线程的 start 函数执行返回会导致对 pthread_exit() 的隐式调用，使用函数的返回值作为线程的退出状态。

       To allow other threads to continue execution, the main thread should terminate by calling pthread_exit() rather than exit(3).
       为了允许其他线程继续执行，主线程应该通过调用 pthread_exit() 而不是 exit(3) 来终止。

       The value pointed to by retval should not be located on the calling thread's stack, since the contents of that stack are undefined after the thread terminates.
       retval 指向的值不应位于调用线程的堆栈上，因为该堆栈的内容在线程终止后未定义。

BUGS
       Currently, there are limitations in the kernel implementation logic for wait(2)ing on a stopped thread group with a dead thread group leader.  This can manifest in problems  such
       as a locked terminal if a stop signal is sent to a foreground process whose thread group leader has already called pthread_exit().

SEE ALSO
       pthread_create(3), pthread_join(3), pthreads(7)

COLOPHON
       This  page  is  part  of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest version of this page, can be
       found at https://www.kernel.org/doc/man-pages/.

Linux                                                                                   2017-09-15                                                                        PTHREAD_EXIT(3)


]]

add_executable(thread-createthread2 thread-createthread2.cpp)

#[[
线程的收尸 pthread_join();

PTHREAD_JOIN(3)                                                                 Linux Programmer's Manual                                                                 PTHREAD_JOIN(3)

NAME
       pthread_join - join with a terminated thread
       为一个终止的线程收尸。

SYNOPSIS
       #include <pthread.h>

       // 相当于进程中的 wait(); 原语。
       // 但是和 wait(); 原语不同,
       // pthread_join();是指定收哪个线程的尸。
       //
       // 第二个参数是一个 void** 类型,
       // 别真给它一个 void** 。
       // 如果 retval 传入的参数为 nullptr, 那就表示只收尸不关心线程的返回状态。
       // 和使用 wait();时候, wait(nullptr); 是一样的道理。
       // 如果想把线程的返回状态加以查看的话, 那就给 retval 一个 void* 的地址,
       // 即一个一级指针的地址, pthread_join();会进行回填这个地址。

       int pthread_join(pthread_t thread, void **retval);

       Compile and link with -pthread.

DESCRIPTION
       The  pthread_join()  function  waits for the thread specified by thread to terminate.  If that thread has already terminated, then pthread_join() returns immediately.  The thread
       specified by thread must be joinable.
       pthread_join() 函数等待 thread 指定的线程终止。如果该线程已经终止，则 pthread_join() 立即返回。 thread 指定的线程必须是可连接的。

       If retval is not NULL, then pthread_join() copies the exit status of the target thread (i.e., the value that the target thread supplied  to  pthread_exit(3))  into  the  location
       pointed to by retval.  If the target thread was canceled, then PTHREAD_CANCELED is placed in the location pointed to by retval.
       如果 retval 不为 NULL，则 pthread_join() 将目标线程的退出状态（即目标线程提供给 pthread_exit(3) 的值）复制到 retval 指向的位置。如果目标线程被取消，则 PTHREAD_CANCELED 被放置在 retval 指向的位置。

       If multiple threads simultaneously try to join with the same thread, the results are undefined.  If the thread calling pthread_join() is canceled, then the target thread will re‐
       main joinable (i.e., it will not be detached).
       如果多个线程同时尝试加入同一个线程，结果是不确定的。如果调用 pthread_join() 的线程被取消，则目标线程将保持可连接状态（即不会被分离）。

RETURN VALUE
       On success, pthread_join() returns 0; on error, it returns an error number.
       成功时，pthread_join() 返回 0；出错时，它返回一个错误号(即返回errno)。

ERRORS
       EDEADLK
              A deadlock was detected (e.g., two threads tried to join with each other); or thread specifies the calling thread.

       EINVAL thread is not a joinable thread.

       EINVAL Another thread is already waiting to join with this thread.

       ESRCH  No thread with the ID thread could be found.

ATTRIBUTES
       For an explanation of the terms used in this section, see attributes(7).

       ┌───────────────┬───────────────┬─────────┐
       │Interface      │ Attribute     │ Value   │
       ├───────────────┼───────────────┼─────────┤
       │pthread_join() │ Thread safety │ MT-Safe │
       └───────────────┴───────────────┴─────────┘

CONFORMING TO
       POSIX.1-2001, POSIX.1-2008.

NOTES
       After a successful call to pthread_join(), the caller is guaranteed that the target thread has terminated.  The caller may then choose to do any clean-up that is  required  after
       termination of the thread (e.g., freeing memory or other resources that were allocated to the target thread).
       成功调用 pthread_join() 后，保证调用者目标线程已终止。然后调用者可以选择在线程终止后进行任何需要的清理（例如，释放分配给目标线程的内存或其他资源）。

       Joining with a thread that has previously been joined results in undefined behavior.
       与先前已连接的线程连接会导致未定义的行为。

       Failure  to join with a thread that is joinable (i.e., one that is not detached), produces a "zombie thread".  Avoid doing this, since each zombie thread consumes some system re‐
       sources, and when enough zombie threads have accumulated, it will no longer be possible to create new threads (or processes).
       与可连接的线程（即未分离的线程）连接失败，会产生“僵尸线程”。避免这样做，因为每个僵尸线程都会消耗一些系统资源，并且当积累了足够的僵尸线程时，将不再可能创建新的线程（或进程）。

       There is no pthreads analog of waitpid(-1, &status, 0), that is, "join with any terminated thread".  If you believe you need this functionality, you probably need to rethink your
       application design.
       没有类似于 waitpid(-1, &status, 0) 的 pthreads 模拟，即“加入任何终止的线程”。如果您认为您需要此功能，您可能需要重新考虑您的应用程序设计。

       All of the threads in a process are peers: any thread can join with any other thread in the process.
       进程中的所有线程都是对等的: 任何线程都可以与进程中的任何其他线程连接。

EXAMPLE
       See pthread_create(3).

SEE ALSO
       pthread_cancel(3), pthread_create(3), pthread_detach(3), pthread_exit(3), pthread_tryjoin_np(3), pthreads(7)

COLOPHON
       This  page  is  part  of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest version of this page, can be
       found at https://www.kernel.org/doc/man-pages/.

Linux                                                                                   2017-09-15                                                                        PTHREAD_JOIN(3)

]]

#[[
加上收尸环节。
]]
add_executable(thread-createthread3 thread-createthread3.cpp)

#[[
线程栈的清理

pthread_cleanup_push();
pthread_cleanup_pop();

这两个函数像 atexit();和 on_exit(); 钩子函数。
但是比钩子函数好一点。
pthread_cleanup_push();就像atexit();钩子函数,

pthread_cleanup_pop();是从钩子上往下取内容, 你可以指定是否执行某个函数。
而atexit();是不能的, 当exit();的时候, 会执行所有的函数。


PTHREAD_CLEANUP_PUSH(3)                                                         Linux Programmer's Manual                                                         PTHREAD_CLEANUP_PUSH(3)

NAME
       pthread_cleanup_push, pthread_cleanup_pop - push and pop thread cancellation clean-up handlers
       推送和弹出线程取消清理处理程序。

SYNOPSIS
       #include <pthread.h>

       // pthread_cleanup_push();的时候需要指定一个函数,
       // 再看一下钩子函数的原型: int atexit(void (*function)(void));


       void pthread_cleanup_push(void (*routine)(void *),
                                 void *arg);


       // pthread_cleanup_pop();的参数 execute 指的是
       // 你在拿到这个钩子函数的时候, 来决定这个钩子函数是否被调用。
       // execute 为真, 那么从钩子上取出来的函数就被调用,
       // 如果 execute 为假的话, 那就说明从钩子上取的函数就不被调用。


       void pthread_cleanup_pop(int execute);

       Compile and link with -pthread.

DESCRIPTION
       These  functions manipulate the calling thread's stack of thread-cancellation clean-up handlers.  A clean-up handler is a function that is automatically executed when a thread is
       canceled (or in various other circumstances described below); it might, for example, unlock a mutex so that it becomes available to other threads in the process.
       这些函数操作调用线程的线程取消清理处理程序堆栈。清理处理程序是一个在线程被取消时（或在下面描述的各种其他情况下）自动执行的函数；例如，它可能会解锁一个互斥锁，以便进程中的其他线程可以使用它。

       The pthread_cleanup_push() function pushes routine onto the top of the stack of clean-up handlers.  When routine is later invoked, it will be given arg as its argument.
       pthread_cleanup_push() 函数将例程推送到清理处理程序堆栈的顶部。当例程稍后被调用时，它会被赋予 arg 作为它的参数。

       The pthread_cleanup_pop() function removes the routine at the top of the stack of clean-up handlers, and optionally executes it if execute is nonzero.
       pthread_cleanup_pop() 函数删除清理处理程序堆栈顶部的例程，如果 execute 非零，则可选择执行它。

       A cancellation clean-up handler is popped from the stack and executed in the following circumstances:
       取消清理处理程序从堆栈中弹出并在以下情况下执行:

       1. When a thread is canceled, all of the stacked clean-up handlers are popped and executed in the reverse of the order in which they were pushed onto the stack.
       1. 当一个线程被取消时，所有堆栈的清理处理程序都被弹出并以它们被压入堆栈的顺序相反的顺序执行。

       2. When a thread terminates by calling pthread_exit(3), all clean-up handlers are executed as described in the preceding point.  (Clean-up handlers are not called if  the  thread
          terminates by performing a return from the thread start function.)
       2. 当一个线程通过调用 pthread_exit(3) 终止时，所有的清理处理程序都按照上一点中的描述执行。 （如果线程通过从线程启动函数执行返回而终止，则不会调用清理处理程序。）

       3. When a thread calls pthread_cleanup_pop() with a nonzero execute argument, the top-most clean-up handler is popped and executed.
       3. 当线程使用非零执行参数调用 pthread_cleanup_pop() 时，将弹出并执行最顶层的清理处理程序。

       POSIX.1  permits  pthread_cleanup_push()  and  pthread_cleanup_pop()  to  be implemented as macros that expand to text containing '{' and '}', respectively.  For this reason, the
       caller must ensure that calls to these functions are paired within the same function, and at the same lexical nesting level.  (In other words, a clean-up handler  is  established
       only during the execution of a specified section of code.)
       POSIX.1 允许将 pthread_cleanup_push() 和 pthread_cleanup_pop() 实现为分别扩展为包含“{”和“}”的文本的宏。出于这个原因，调用者必须确保对这些函数的调用在同一个函数内成对出现，并且处于相同的词法嵌套级别。 （换句话说，仅在执行指定的代码段期间才建立清理处理程序。）

       Calling  longjmp(3)  (siglongjmp(3)) produces undefined results if any call has been made to pthread_cleanup_push() or pthread_cleanup_pop() without the matching call of the pair
       since the jump buffer was filled by setjmp(3) (sigsetjmp(3)).  Likewise, calling longjmp(3) (siglongjmp(3)) from inside a clean-up handler produces undefined results  unless  the
       jump buffer was also filled by setjmp(3) (sigsetjmp(3)) inside the handler.
       如果在没有匹配调用的情况下对 pthread_cleanup_push() 或 pthread_cleanup_pop() 进行了任何调用，则调用 longjmp(3) (siglongjmp(3)) 会产生未定义的结果，因为 setjmp(3) (sigsetjmp(3) ))。同样，从清理处理程序内部调用 longjmp(3) (siglongjmp(3)) 会产生未定义的结果，除非跳转缓冲区也由处理程序内部的 setjmp(3) (sigsetjmp(3)) 填充。

RETURN VALUE
       These functions do not return a value.
       这些函数不返回值。

ERRORS
       There are no errors.
       没有错误。

ATTRIBUTES
       For an explanation of the terms used in this section, see attributes(7).

       ┌────────────────────────┬───────────────┬─────────┐
       │Interface               │ Attribute     │ Value   │
       ├────────────────────────┼───────────────┼─────────┤
       │pthread_cleanup_push(), │ Thread safety │ MT-Safe │
       │pthread_cleanup_pop()   │               │         │
       └────────────────────────┴───────────────┴─────────┘

CONFORMING TO
       POSIX.1-2001, POSIX.1-2008.

NOTES
       On Linux, the pthread_cleanup_push() and pthread_cleanup_pop() functions are implemented as macros that expand to text containing '{' and  '}',  respectively.   This  means  that
       variables declared within the scope of paired calls to these functions will be visible within only that scope.
       在 Linux 上，pthread_cleanup_push() 和 pthread_cleanup_pop() 函数作为宏实现，分别扩展为包含“{”和“}”的文本。这意味着在对这些函数的成对调用范围内声明的变量将仅在该范围内可见。

       POSIX.1 says that the effect of using return, break, continue, or goto to prematurely leave a block bracketed pthread_cleanup_push() and pthread_cleanup_pop() is undefined.  Por‐
       table applications should avoid doing this.
       POSIX.1 表示使用 return、break、continue 或 goto 过早地离开包含 pthread_cleanup_push() 和 pthread_cleanup_pop() 的块的效果是未定义的。便携式应用程序应该避免这样做。

SEE ALSO
       pthread_cancel(3), pthread_cleanup_push_defer_np(3), pthread_setcancelstate(3), pthread_testcancel(3), pthreads(7)

COLOPHON
       This page is part of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest version of  this  page,  can  be
       found at https://www.kernel.org/doc/man-pages/.

Linux                                                                                   2019-03-06                                                                PTHREAD_CLEANUP_PUSH(3)


]]
#[[
在 linux 上 pthread_cleanup_push(); 和 pthread_cleanup_pop(); 是宏函数。
]]
add_executable(thread-cleanup1 thread-cleanup1.cpp)
#[[
使用 lambda表达式也是可以的。
]]
add_executable(thread-cleanup2 thread-cleanup2.cpp)

#[[

线程的取消选项

一个线程正在跑, 那怎么收尸收回来?
先取消, 再收尸。
pthread_cancel();

PTHREAD_CANCEL(3)                                                               Linux Programmer's Manual                                                               PTHREAD_CANCEL(3)

NAME
       pthread_cancel - send a cancellation request to a thread
       向线程发送取消请求。

SYNOPSIS
       #include <pthread.h>

       // 取消 thread 线程。
       // 返回值是是否取消了。

       int pthread_cancel(pthread_t thread);

       Compile and link with -pthread.

DESCRIPTION
       The  pthread_cancel() function sends a cancellation request to the thread thread.  Whether and when the target thread reacts to the cancellation request depends on two attributes
       that are under the control of that thread: its cancelability state and type.
       pthread_cancel() 函数向线程线程发送取消请求。目标线程是否以及何时对取消请求做出反应取决于该线程控制下的两个属性: 其可取消性状态和类型。

       A thread's cancelability state, determined by pthread_setcancelstate(3), can be enabled (the default for new threads) or disabled.  If a thread has disabled cancellation, then  a
       cancellation request remains queued until the thread enables cancellation.  If a thread has enabled cancellation, then its cancelability type determines when cancellation occurs.
       线程的可取消状态由 pthread_setcancelstate(3) 确定，可以启用（新线程的默认设置）或禁用。
       如果线程已禁用取消，则取消请求将保持排队状态，直到该线程启用取消。如果线程已启用取消，则其可取消性类型确定何时发生取消。

       A thread's cancellation type, determined by pthread_setcanceltype(3), may be either asynchronous or deferred (the default for new threads).  Asynchronous cancelability means that
       the thread can be canceled at any time (usually immediately, but the system does not guarantee this).  Deferred cancelability means that cancellation will be  delayed  until  the
       thread next calls a function that is a cancellation point.  A list of functions that are or may be cancellation points is provided in pthreads(7).
       线程的取消类型由 pthread_setcanceltype(3) 确定，可以是异步的或延迟的（新线程的默认设置）。
       异步可取消性意味着线程可以随时取消（通常是立即取消，但系统不保证这一点）。
       延迟可取消性意味着取消将被延迟，直到线程下一次调用作为取消点的函数。 pthreads(7) 中提供了是或可能是取消点的函数列表。

       When a cancellation requested is acted on, the following steps occur for thread (in this order):
       当对取消请求采取行动时，线程将执行以下步骤（按此顺序）:

       1. Cancellation clean-up handlers are popped (in the reverse of the order in which they were pushed) and called.  (See pthread_cleanup_push(3).)
       1. 取消清理处理程序被弹出（与它们被推送的顺序相反）并被调用。 （请参阅 pthread_cleanup_push(3)。）

       2. Thread-specific data destructors are called, in an unspecified order.  (See pthread_key_create(3).)
       2. 以未指定的顺序调用特定于线程的数据析构函数。 （请参阅 pthread_key_create(3)。）

       3. The thread is terminated.  (See pthread_exit(3).)
       3. 线程终止。 （请参阅 pthread_exit(3)。）

       The  above steps happen asynchronously with respect to the pthread_cancel() call; the return status of pthread_cancel() merely informs the caller whether the cancellation request
       was successfully queued.
       上述步骤与 pthread_cancel() 调用异步发生； pthread_cancel() 的返回状态只是通知调用者取消请求是否成功排队。

       After a canceled thread has terminated, a join with that thread using pthread_join(3) obtains PTHREAD_CANCELED as the thread's exit status.  (Joining with a thread  is  the  only
       way to know that cancellation has completed.)
       取消的线程终止后，使用 pthread_join(3) 与该线程的连接获得 PTHREAD_CANCELED 作为线程的退出状态。 （使用线程加入是知道取消已完成的唯一方法。）

RETURN VALUE
       On success, pthread_cancel() returns 0; on error, it returns a nonzero error number.
       成功时，pthread_cancel() 返回 0；出错时，它返回一个非零错误号(errno)。

ERRORS
       ESRCH  No thread with the ID thread could be found.
       ESRCH 找不到带有 ID 线程的线程。

ATTRIBUTES
       For an explanation of the terms used in this section, see attributes(7).

       ┌─────────────────┬───────────────┬─────────┐
       │Interface        │ Attribute     │ Value   │
       ├─────────────────┼───────────────┼─────────┤
       │pthread_cancel() │ Thread safety │ MT-Safe │
       └─────────────────┴───────────────┴─────────┘

CONFORMING TO
       POSIX.1-2001, POSIX.1-2008.

NOTES
       On  Linux,  cancellation is implemented using signals.  Under the NPTL threading implementation, the first real-time signal (i.e., signal 32) is used for this purpose.  On Linux‐
       Threads, the second real-time signal is used, if real-time signals are available, otherwise SIGUSR2 is used.
       在 Linux 上，取消是使用信号实现的。
       在 NPTL 线程实现下，第一个实时信号（即信号 32）用于此目的。在 Linux 线程上，如果实时信号可用，则使用第二个实时信号，否则使用 SIGUSR2。

SEE ALSO
       pthread_cleanup_push(3), pthread_create(3), pthread_exit(3), pthread_join(3), pthread_key_create(3), pthread_setcancelstate(3), pthread_setcanceltype(3), pthread_testcancel(3),
       pthreads(7)

COLOPHON
       This page is part of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest version of this page, can be
       found at https://www.kernel.org/doc/man-pages/.

Linux                                                                                   2019-03-06                                                                      PTHREAD_CANCEL(3)

]]
#[[
线程的取消   pthread_cancel();
线程取消由取消选项来指定。

线程取消有2种状态:
    1. 允许, 允许取消又分为了2种:
        1.1 异步取消
        1.2 推迟取消, 推迟取消是默认的线程选项。
            什么是推迟取消?
            推迟到cancel点再去响应。
            cancel点: POSIX定义的cancel点, 都是可能引发阻塞的系统调用。
            而非系统调用到底是不是cancel点, 各个平台的定义还是有区别的, 要细看相关的平台的规定。

    2. 不允许, 没有线程可以取消这个线程。


PTHREAD_SETCANCELSTATE(3)                                                       Linux Programmer's Manual                                                       PTHREAD_SETCANCELSTATE(3)

NAME
       pthread_setcancelstate, pthread_setcanceltype - set cancelability state and type
       设置可取消状态和类型。

SYNOPSIS
       #include <pthread.h>

       // 设置线程的取消状态, 即设置线程是否允许取消。

       int pthread_setcancelstate(int state, int *oldstate);


       // 设置线程的取消类型, 异步取消还是推迟取消。

       int pthread_setcanceltype(int type, int *oldtype);

       Compile and link with -pthread.

DESCRIPTION
       The  pthread_setcancelstate()  sets  the cancelability state of the calling thread to the value given in state.  The previous cancelability state of the thread is returned in the
       buffer pointed to by oldstate.  The state argument must have one of the following values:
       pthread_setcancelstate() 将调用线程的可取消状态设置为 state 中给定的值。线程先前的可取消状态在 oldstate 指向的缓冲区中返回。 state 参数必须具有以下值之一:

       PTHREAD_CANCEL_ENABLE
              The thread is cancelable.  This is the default cancelability state in all new threads, including the initial thread.  The thread's cancelability  type  determines  when  a
              cancelable thread will respond to a cancellation request.
              线程是可取消的。这是所有新线程（包括初始线程）中的默认可取消状态。线程的可取消性类型决定了可取消线程何时响应取消请求。

       PTHREAD_CANCEL_DISABLE
              The thread is not cancelable.  If a cancellation request is received, it is blocked until cancelability is enabled.
              该线程不可取消。如果收到取消请求，它将被阻止，直到启用可取消性。

       The pthread_setcanceltype() sets the cancelability type of the calling thread to the value given in type.  The previous cancelability type of the thread is returned in the buffer
       pointed to by oldtype.  The type argument must have one of the following values:
       pthread_setcanceltype() 将调用线程的可取消性类型设置为 type 中给定的值。线程的先前可取消性类型在 oldtype 指向的缓冲区中返回。类型参数必须具有以下值之一:

       PTHREAD_CANCEL_DEFERRED
              A cancellation request is deferred until the thread next calls a function that is a cancellation point (see pthreads(7)).  This is the default cancelability  type  in  all
              new threads, including the initial thread.
              取消请求被推迟到线程下一次调用作为取消点的函数（请参阅 pthreads(7)）。这是所有新线程（包括初始线程）中的默认可取消性类型。

              Even  with  deferred  cancellation, a cancellation point in an asynchronous signal handler may still be acted upon and the effect is as if it was an asynchronous cancella‐
              tion.
              即使使用延迟取消，异步信号处理程序中的取消点仍可能被操作，其效果就像是异步取消。

       PTHREAD_CANCEL_ASYNCHRONOUS
              The thread can be canceled at any time.  (Typically, it will be canceled immediately upon receiving a cancellation request, but the system doesn't guarantee this.)
              线程可以随时取消。 （一般情况下，收到取消请求会立即取消，但系统不保证。）

       The set-and-get operation performed by each of these functions is atomic with respect to other threads in the process calling the same function.
       由这些函数中的每一个执行的设置和获取操作相对于调用相同函数的进程中的其他线程而言是原子的。

RETURN VALUE
       On success, these functions return 0; on error, they return a nonzero error number.
       成功时，这些函数返回 0；出错时，它们返回一个非零错误号。

ERRORS
       The pthread_setcancelstate() can fail with the following error:
       pthread_setcancelstate() 可能会失败并出现以下错误:

       EINVAL Invalid value for state.
       EINVAL 状态值无效。

       The pthread_setcanceltype() can fail with the following error:
       pthread_setcanceltype() 可能会失败并出现以下错误:

       EINVAL Invalid value for type.
       EINVAL 类型的无效值。

ATTRIBUTES
       For an explanation of the terms used in this section, see attributes(7).

       ┌──────────────────────────┬─────────────────────┬─────────┐
       │Interface                 │ Attribute           │ Value   │
       ├──────────────────────────┼─────────────────────┼─────────┤
       │pthread_setcancelstate(), │ Thread safety       │ MT-Safe │
       │pthread_setcanceltype()   │                     │         │
       ├──────────────────────────┼─────────────────────┼─────────┤
       │pthread_setcancelstate(), │ Async-cancel-safety │ AC-Safe │
       │pthread_setcanceltype()   │                     │         │
       └──────────────────────────┴─────────────────────┴─────────┘
CONFORMING TO
       POSIX.1-2001, POSIX.1-2008.

NOTES
       For details of what happens when a thread is canceled, see pthread_cancel(3).
       有关取消线程时发生的情况的详细信息，请参阅 pthread_cancel(3)。

       Briefly disabling cancelability is useful if a thread performs some critical action that must not be interrupted by a cancellation request.  Beware of disabling cancelability for
       long periods, or around operations that may block for long periods, since that will render the thread unresponsive to cancellation requests.
       如果线程执行一些不能被取消请求中断的关键操作，则简要禁用可取消性很有用。当心长时间禁用可取消性，或在可能长时间阻塞的操作周围禁用，因为这会使线程对取消请求无响应。

   Asynchronous cancelability
   异步可取消性
       Setting  the  cancelability type to PTHREAD_CANCEL_ASYNCHRONOUS is rarely useful.  Since the thread could be canceled at any time, it cannot safely reserve resources (e.g., allo‐
       cating memory with malloc(3)), acquire mutexes, semaphores, or locks, and so on.  Reserving resources is unsafe because the application has no way of knowing what  the  state  of
       these  resources  is when the thread is canceled; that is, did cancellation occur before the resources were reserved, while they were reserved, or after they were released?  Fur‐
       thermore, some internal data structures (e.g., the linked list of free blocks managed by the malloc(3) family of functions) may be left in an inconsistent state  if  cancellation
       occurs in the middle of the function call.  Consequently, clean-up handlers cease to be useful.
       将可取消性类型设置为 PTHREAD_CANCEL_ASYNCHRONOUS 很少有用。由于线程可能随时被取消，因此它无法安全地保留资源（例如，使用 malloc(3) 分配内存）、获取互斥锁、信号量或锁等。保留资源是不安全的，因为当线程被取消时，应用程序无法知道这些资源的状态；也就是说，取消是在资源保留之前、保留期间还是释放之后发生？此外，如果取消发生在函数调用的中间，一些内部数据结构（例如，由 malloc(3) 系列函数管理的空闲块的链表）可能会处于不一致的状态。因此，清理处理程序不再有用。

       Functions  that  can  be  safely  asynchronously canceled are called async-cancel-safe functions.  POSIX.1-2001 and POSIX.1-2008 require only that pthread_cancel(3), pthread_set‐
       cancelstate(), and pthread_setcanceltype() be async-cancel-safe.  In general, other library functions can't be safely called from an asynchronously cancelable thread.
       可以安全异步取消的函数称为异步取消安全函数。 POSIX.1-2001 和 POSIX.1-2008 只要求 pthread_cancel(3)、pthread_set-cancelstate() 和 pthread_setcanceltype() 是异步取消安全的。通常，不能从异步可取消线程安全地调用其他库函数。

       One of the few circumstances in which asynchronous cancelability is useful is for cancellation of a thread that is in a pure compute-bound loop.
       异步可取消性有用的少数情况之一是取消纯计算绑定循环中的线程。

   Portability notes
   便携性说明
       The Linux threading implementations permit the oldstate argument of pthread_setcancelstate() to be NULL, in which case the information about the previous cancelability  state  is
       not returned to the caller.  Many other implementations also permit a NULL oldstat argument, but POSIX.1 does not specify this point, so portable applications should always spec‐
       ify a non-NULL value in oldstate.  A precisely analogous set of statements applies for the oldtype argument of pthread_setcanceltype().
       Linux 线程实现允许 pthread_setcancelstate() 的 oldstate 参数为 NULL，在这种情况下，有关先前可取消状态的信息不会返回给调用者。许多其他实现也允许 NULL oldstat 参数，但 POSIX.1 没有指定这一点，因此可移植应用程序应始终在 oldstate 中指定非 NULL 值。一组精确类似的语句适用于 pthread_setcanceltype() 的 oldtype 参数。

EXAMPLE
       See pthread_cancel(3).

SEE ALSO
       pthread_cancel(3), pthread_cleanup_push(3), pthread_testcancel(3), pthreads(7)

COLOPHON
       This page is part of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest version of  this  page,  can  be
       found at https://www.kernel.org/doc/man-pages/.

Linux                                                                                   2019-10-10                                                              PTHREAD_SETCANCELSTATE(3)


PTHREAD_TESTCANCEL(3)                                                           Linux Programmer's Manual                                                           PTHREAD_TESTCANCEL(3)

NAME
       pthread_testcancel - request delivery of any pending cancellation request
       请求交付任何待处理的取消请求。

SYNOPSIS
       #include <pthread.h>

       // pthread_testcancel();这个函数本身就是一个取消点。
       // 这个函数什么都不做, 就是一个取消点。

       void pthread_testcancel(void);

       Compile and link with -pthread.

DESCRIPTION
       Calling  pthread_testcancel()  creates a cancellation point within the calling thread, so that a thread that is otherwise executing code that contains no cancellation points will
       respond to a cancellation request.
       调用 pthread_testcancel() 在调用线程中创建一个取消点，以便正在执行不包含取消点的代码的线程将响应取消请求。

       If cancelability is disabled (using pthread_setcancelstate(3)), or no cancellation request is pending, then a call to pthread_testcancel() has no effect.
       如果禁用了可取消性（使用 pthread_setcancelstate(3)），或者没有未决取消请求，则对 pthread_testcancel() 的调用无效。

RETURN VALUE
       This function does not return a value.  If the calling thread is canceled as a consequence of a call to this function, then the function does not return.
       此函数不返回值。如果调用线程因调用此函数而被取消，则该函数不会返回。

ERRORS
       This function always succeeds.
       此函数总是成功。

ATTRIBUTES
       For an explanation of the terms used in this section, see attributes(7).

       ┌─────────────────────┬───────────────┬─────────┐
       │Interface            │ Attribute     │ Value   │
       ├─────────────────────┼───────────────┼─────────┤
       │pthread_testcancel() │ Thread safety │ MT-Safe │
       └─────────────────────┴───────────────┴─────────┘
CONFORMING TO
       POSIX.1-2001, POSIX.1-2008.

EXAMPLE
       See pthread_cleanup_push(3).

SEE ALSO
       pthread_cancel(3), pthread_cleanup_push(3), pthread_setcancelstate(3), pthreads(7)

COLOPHON
       This page is part of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest version of  this  page,  can  be
       found at https://www.kernel.org/doc/man-pages/.

Linux                                                                                   2017-09-15                                                                  PTHREAD_TESTCANCEL(3)

]]

#[[
线程分离
pthread_detach();

PTHREAD_DETACH(3)                                                               Linux Programmer's Manual                                                               PTHREAD_DETACH(3)

NAME
       pthread_detach - detach a thread
       分离一个线程。

SYNOPSIS
       #include <pthread.h>

       // 如果一个线程分离了, 那么就不能为这个线程收尸。
       // 这个线程分离后运行终止时, 资源会自动释放回收系统。

       int pthread_detach(pthread_t thread);

       Compile and link with -pthread.

DESCRIPTION
       The  pthread_detach()  function marks the thread identified by thread as detached.  When a detached thread terminates, its resources are automatically released back to the system
       without the need for another thread to join with the terminated thread.
       pthread_detach() 函数将线程标识的线程标记为已分离。当一个分离的线程终止时，它的资源会自动释放回系统，而无需另一个线程与终止的线程连接。

       Attempting to detach an already detached thread results in unspecified behavior.
       尝试分离已分离的线程会导致未指定的行为。

RETURN VALUE
       On success, pthread_detach() returns 0; on error, it returns an error number.
       成功时，pthread_detach() 返回 0；出错时，它返回一个错误号。

ERRORS
       EINVAL thread is not a joinable thread.
       EINVAL 线程不是可连接的线程。

       ESRCH  No thread with the ID thread could be found.
       ESRCH 找不到带有 ID 线程的线程。

ATTRIBUTES
       For an explanation of the terms used in this section, see attributes(7).

       ┌─────────────────┬───────────────┬─────────┐
       │Interface        │ Attribute     │ Value   │
       ├─────────────────┼───────────────┼─────────┤
       │pthread_detach() │ Thread safety │ MT-Safe │
       └─────────────────┴───────────────┴─────────┘

CONFORMING TO
       POSIX.1-2001, POSIX.1-2008.

NOTES
       Once a thread has been detached, it can't be joined with pthread_join(3) or be made joinable again.
       一旦线程被分离，它就不能与 pthread_join(3) 连接或再次成为可连接的。

       A new thread can be created in a detached state using pthread_attr_setdetachstate(3) to set the detached attribute of the attr argument of pthread_create(3).
       可以使用 pthread_attr_setdetachstate(3) 在分离状态下创建新线程，以设置 pthread_create(3) 的 attr 参数的分离属性。

       The detached attribute merely determines the behavior of the system when the thread terminates; it does not prevent the thread from being terminated if the process terminates us‐
       ing exit(3) (or equivalently, if the main thread returns).
       detached 属性仅仅决定了线程终止时系统的行为；如果进程使用 exit(3) 终止（或等效地，如果主线程返回），它不会阻止线程被终止。

       Either  pthread_join(3) or pthread_detach() should be called for each thread that an application creates, so that system resources for the thread can be released.  (But note that
       the resources of any threads for which one of these actions has not been done will be freed when the process terminates.)
       应为应用程序创建的每个线程调用 pthread_join(3) 或 pthread_detach()，以便可以释放线程的系统资源。 （但请注意，当进程终止时，任何未执行这些操作之一的线程的资源都将被释放。）

EXAMPLE
       The following statement detaches the calling thread:
       以下语句分离调用线程:
       即分离线程自己。

           pthread_detach(pthread_self());

SEE ALSO
       pthread_attr_setdetachstate(3), pthread_cancel(3), pthread_create(3), pthread_exit(3), pthread_join(3), pthreads(7)

COLOPHON
       This page is part of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest version of  this  page,  can  be
       found at https://www.kernel.org/doc/man-pages/.

Linux                                                                                   2017-09-15                                                                      PTHREAD_DETACH(3)

]]


add_executable(thread-primer1 thread-primer1.cpp)
add_executable(thread-primer2 thread-primer2.cpp)
#[[
2版本中有竞争。
]]
add_executable(thread-primer3 thread-primer3.cpp)
#[[
3 版本中的 pthread_create(); 第一个传参有问题。
修改了这个问题后, 还有一个问题是 段错误。目前还不知道问题在哪?
在 4 版本中进行找 BUG。
]]
add_executable(thread-primer4 thread-primer4.cpp)
#[[
在 4 版本中 BUG 找到了, 不能挂钩子函数上, 而是需要明明白白的在 return 语句(或者exit();)之前
执行 pthread_join();来收尸。
下面的 5 版本是正确的版本。
]]
add_executable(thread-primer5 thread-primer5.cpp)
#[[
6 版本将 5 版本的各种注释删除了。
]]
add_executable(thread-primer6 thread-primer6.cpp)
#[[
7 版本是 6 版本 在pthread_create();中使用lambda表达式。
todo 笔记: 编译运行也是没有任何问题的。确实 lambda表达式还是非常的实用的！
]]
add_executable(thread-primer7 thread-primer7.cpp)


#[[

ps axf

ps axm

ps ax -L

编译程序, 出现警告: 如果能解释出来, 那就可以把这个警告忽略掉。
其它情况下, 要把程序调到编译没有警告为止。

]]

#[[
不是像之前那样使用数组了, 而是使用动态分配的内存。
8 这个版本是可以正确运行出来的。但是设计上不太好, 见 9 版本的改进。
]]
add_executable(thread-primer8 thread-primer8.cpp)
#[[
9 这个版本是一个好的设计方式, 9 的设计的原则就是:
谁申请, 谁释放的原则, 也就是尽量将资源的申请与资源的释放放在同一个函数中,
如果申请和释放放在同一个函数中, 那么就一定要放在同一个模块内！！！！
9 的这个版本的设计为一个比 8 版本更优的设计。
]]
add_executable(thread-primer9 thread-primer9.cpp)
#[[
但是 9 的版本也不是一个好的设计, 因为创建了太多的线程来完成一个简单的任务。

liuyou@liuyou-virtual-machine:~/CLionProjects/LinuxEnvironmentProgramming2$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 15374
max locked memory       (kbytes, -l) 65536
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 15374
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
liuyou@liuyou-virtual-machine:~/CLionProjects/LinuxEnvironmentProgramming2$

当前的进程能创建多少个线程, 也就是能在线程空间里面创建多少个 8MB 大小的栈。

]]


#[[
线程竞争的小程序
]]
add_executable(thread-fileadd1 thread-fileadd1.cpp)
#[[
上面的程序直接段错误。是线程竞争造成的。
那么如果解决呢?
那就需要线程同步的知识了。

互斥量 mutex
]]

#[[
    mutex
        互斥量: 某一个资源(一个对象, 一个变量, 一个对象中的字段等等)
               有可能在多个线程中使用时发生竞争和冲突,
               那么就在每个线程中使用这个资源的时候加上互斥量,
               这是一种锁的机制。
        pthread_mutex_init();
        pthread_mutex_destroy();

PTHREAD_MUTEX_INIT(3P)                                                          POSIX Programmer's Manual                                                          PTHREAD_MUTEX_INIT(3P)

PROLOG 序言
       This  manual  page  is  part  of the POSIX Programmer's Manual.  The Linux implementation of this interface may differ (consult the corresponding Linux manual page for details of
       Linux behavior), or the interface may not be implemented on Linux.
       本手册页是 POSIX 程序员手册的一部分。此接口的 Linux 实现可能不同（有关 Linux 行为的详细信息，请参阅相应的 Linux 手册页），或者该接口可能未在 Linux 上实现。

NAME
       pthread_mutex_init — destroy and initialize a mutex

SYNOPSIS
       #include <pthread.h>

       int pthread_mutex_init(pthread_mutex_t *restrict mutex,
           const pthread_mutexattr_t *restrict attr);
       pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

DESCRIPTION
       Refer to pthread_mutex_destroy().
       请参阅 pthread_mutex_destroy()。

COPYRIGHT
       Portions of this text are reprinted and reproduced in electronic form from IEEE Std 1003.1, 2013 Edition, Standard for Information Technology -- Portable Operating System  Inter‐
       face  (POSIX),  The  Open  Group  Base  Specifications  Issue  7,  Copyright  (C) 2013 by the Institute of Electrical and Electronics Engineers, Inc and The Open Group.  (This is
       POSIX.1-2008 with the 2013 Technical Corrigendum 1 applied.) In the event of any discrepancy between this version and the original IEEE and The Open Group Standard, the  original
       IEEE and The Open Group Standard is the referee document. The original Standard can be obtained online at http://www.unix.org/online.html .

       Any  typographical  or  formatting errors that appear in this page are most likely to have been introduced during the conversion of the source files to man page format. To report
       such errors, see https://www.kernel.org/doc/man-pages/reporting_bugs.html .

IEEE/The Open Group                                                                        2013                                                                    PTHREAD_MUTEX_INIT(3P)


PTHREAD_MUTEX_DESTROY(3P)                                         POSIX Programmer's Manual                                         PTHREAD_MUTEX_DESTROY(3P)

PROLOG
       This  manual  page  is  part of the POSIX Programmer's Manual.  The Linux implementation of this interface may differ (consult the corresponding Linux
       manual page for details of Linux behavior), or the interface may not be implemented on Linux.
       本手册页是 POSIX 程序员手册的一部分。此接口的 Linux 实现可能不同（有关 Linux 行为的详细信息，请参阅相应的 Linux 手册页），或者该接口可能未在 Linux 上实现。

NAME
       pthread_mutex_destroy, pthread_mutex_init — destroy and initialize a mutex

SYNOPSIS
       #include <pthread.h>

       int pthread_mutex_destroy(pthread_mutex_t *mutex);

todo 笔记: // 动态初始化和静态初始化会放在不同的环境下使用,
       // 例如: 互斥量 就是凭空定义出来的变量的话,
       // 那么使用静态初始化的方式非常简单, 用的是默认的互斥锁的属性。
       //
       // 而如果希望更改属性初始化一个互斥锁,
       // 或者当前这个互斥锁是一个结构体或者class的一个字段, 那就没有办法用静态方式初始化。
       //
       // 通常会设计一个 Mutex 类来将 pthread_mutex_t 互斥量封装起来。
       // 见 陈硕老师书籍、游双老师书籍中各有封装 互斥量 的 class。


       int pthread_mutex_init(pthread_mutex_t *restrict mutex,
           const pthread_mutexattr_t *restrict attr);

       // 静态初始化一个互斥锁。
       pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

DESCRIPTION
       The pthread_mutex_destroy() function shall destroy the mutex object referenced by mutex; the mutex object becomes, in effect, uninitialized. An imple‐
       mentation may cause pthread_mutex_destroy() to set the object referenced by mutex to an invalid value.
       pthread_mutex_destroy() 函数会销毁mutex 引用的mutex 对象；互斥对象实际上未初始化。实现可能会导致 pthread_mutex_destroy() 将 mutex 引用的对象设置为无效值。

       A  destroyed mutex object can be reinitialized using pthread_mutex_init(); the results of otherwise referencing the object after it has been destroyed
       are undefined.
       被破坏的互斥对象可以使用 pthread_mutex_init() 重新初始化；在对象被销毁后以其他方式引用对象的结果是未定义的。

       It shall be safe to destroy an initialized mutex that is unlocked.  Attempting to destroy a locked mutex or a mutex that is referenced  (for  example,
       while being used in a pthread_cond_timedwait() or pthread_cond_wait()) by another thread results in undefined behavior.
       销毁未锁定的已初始化互斥锁应该是安全的。尝试销毁锁定的互斥锁或另一个线程引用的互斥锁（例如，在 pthread_cond_timedwait() 或 pthread_cond_wait() 中使用时）会导致未定义的行为。

       The  pthread_mutex_init()  function  shall  initialize  the mutex referenced by mutex with attributes specified by attr.  If attr is NULL, the default
       mutex attributes are used; the effect shall be the same as passing the address of a default mutex attributes object. Upon  successful  initialization,
       the state of the mutex becomes initialized and unlocked.
       pthread_mutex_init() 函数应使用 attr 指定的属性初始化由 mutex 引用的互斥锁。如果 attr 为 NULL，则使用默认互斥属性；效果应与传递默认互斥体属性对象的地址相同。成功初始化后，互斥锁的状态将被初始化和解锁。

       Only  mutex  itself  may  be  used  for  performing  synchronization.  The  result  of  referring to copies of mutex in calls to pthread_mutex_lock(),
       pthread_mutex_trylock(), pthread_mutex_unlock(), and pthread_mutex_destroy() is undefined.
       只有互斥锁本身可以用于执行同步。在调用 pthread_mutex_lock()、pthread_mutex_trylock()、pthread_mutex_unlock() 和 pthread_mutex_destroy() 时引用互斥锁副本的结果未定义。

       Attempting to initialize an already initialized mutex results in undefined behavior.
       尝试初始化已初始化的互斥锁会导致未定义的行为。

       In cases where default mutex attributes are appropriate, the macro PTHREAD_MUTEX_INITIALIZER can be used to initialize mutexes. The  effect  shall  be
       equivalent  to  dynamic  initialization  by a call to pthread_mutex_init() with parameter attr specified as NULL, except that no error checks are per‐
       formed.
       在默认互斥体属性合适的情况下，宏 PTHREAD_MUTEX_INITIALIZER 可用于初始化互斥体。除了不执行错误检查外，效果应等同于通过调用 pthread_mutex_init() 并将参数 attr 指定为 NULL 进行动态初始化。

       The behavior is undefined if the value specified by the mutex argument to pthread_mutex_destroy() does not refer to an initialized mutex.
       如果 pthread_mutex_destroy() 的 mutex 参数指定的值不引用已初始化的互斥锁，则行为未定义。

       The behavior is undefined if the value specified by the attr argument to pthread_mutex_init() does  not  refer  to  an  initialized  mutex  attributes
       object.
       如果 pthread_mutex_init() 的 attr 参数指定的值不引用初始化的互斥属性对象，则行为未定义。

RETURN VALUE
       If successful, the pthread_mutex_destroy() and pthread_mutex_init() functions shall return zero; otherwise, an error number shall be returned to indi‐
       cate the error.
       如果成功，pthread_mutex_destroy() 和 pthread_mutex_init() 函数应返回零；否则，将返回错误编号以指示错误。

ERRORS
       The pthread_mutex_init() function shall fail if:
       如果出现以下情况，pthread_mutex_init() 函数将失败:

       EAGAIN The system lacked the necessary resources (other than memory) to initialize another mutex.
       系统缺乏必要的资源（内存除外）来初始化另一个互斥锁。

       ENOMEM Insufficient memory exists to initialize the mutex.
       内存不足，无法初始化互斥锁。

       EPERM  The caller does not have the privilege to perform the operation.
       调用者没有执行操作的权限。

       The pthread_mutex_init() function may fail if:
       如果出现以下情况，pthread_mutex_init() 函数可能会失败:

       EINVAL The attributes object referenced by attr has the robust mutex attribute set without the process-shared attribute being set.
       attr 引用的属性对象具有健壮的互斥属性集，但未设置进程共享属性。

       These functions shall not return an error code of [EINTR].
       这些函数不应返回 [EINTR] 的错误代码。

       The following sections are informative.
       以下部分是信息性的。

EXAMPLES
       None.

APPLICATION USAGE
       None.

RATIONALE 基本原理
       If an implementation detects that the value specified by the mutex argument to pthread_mutex_destroy() does not refer to an initialized mutex,  it  is
       recommended that the function should fail and report an [EINVAL] error.
       如果实现检测到 pthread_mutex_destroy() 的 mutex 参数指定的值不引用已初始化的互斥锁，则建议该函数应失败并报告 [EINVAL] 错误。

       If  an  implementation  detects  that  the value specified by the mutex argument to pthread_mutex_destroy() or pthread_mutex_init() refers to a locked
       mutex or a mutex that is referenced (for example, while being used in a pthread_cond_timedwait() or pthread_cond_wait()) by another thread, or detects
       that  the  value  specified  by the mutex argument to pthread_mutex_init() refers to an already initialized mutex, it is recommended that the function
       should fail and report an [EBUSY] error.
       如果实现检测到 pthread_mutex_destroy() 或 pthread_mutex_init() 的 mutex 参数指定的值指的是锁定的互斥锁或被另一个引用的互斥锁（例如，在 pthread_cond_timedwait() 或 pthread_cond_wait() 中使用时）线程，或者检测到 pthread_mutex_init() 的 mutex 参数指定的值是指已经初始化的互斥锁，建议该函数应该失败并报告 [EBUSY] 错误。

       If an implementation detects that the value specified by the attr argument to pthread_mutex_init() does not refer to an initialized  mutex  attributes
       object, it is recommended that the function should fail and report an [EINVAL] error.
       如果实现检测到 pthread_mutex_init() 的 attr 参数指定的值不引用初始化的互斥属性对象，则建议该函数应失败并报告 [EINVAL] 错误。

   Alternate Implementations Possible
   可能的替代实现
       This  volume  of POSIX.1‐2008 supports several alternative implementations of mutexes.  An implementation may store the lock directly in the object of
       type pthread_mutex_t.  Alternatively, an implementation may store the lock in the heap and merely store a pointer, handle, or unique ID in  the  mutex
       object.   Either  implementation  has  advantages  or may be required on certain hardware configurations. So that portable code can be written that is
       invariant to this choice, this volume of POSIX.1‐2008 does not define assignment or equality for this type, and it uses  the  term  ``initialize''  to
       reinforce the (more restrictive) notion that the lock may actually reside in the mutex object itself.
       POSIX.1-2008 的这一卷支持互斥体的几种替代实现。一个实现可以将锁直接存储在 pthread_mutex_t 类型的对象中。或者，一个实现可以将锁存储在堆中，而只在互斥对象中存储一个指针、句柄或唯一 ID。两种实现都有优势，或者可能需要某些硬件配置。为了可以编写对这种选择保持不变的可移植代码，本 POSIX.1-2008 卷没有定义这种类型的赋值或相等，它使用术语“初始化”来强化（更严格的）概念锁实际上可能驻留在互斥对象本身中。

       Note that this precludes an over-specification of the type of the mutex or condition variable and motivates the opaqueness of the type.
       请注意，这排除了对互斥锁或条件变量类型的过度规范，并激发了类型的不透明性。

       An  implementation  is  permitted, but not required, to have pthread_mutex_destroy() store an illegal value into the mutex. This may help detect erro‐
       neous programs that try to lock (or otherwise reference) a mutex that has already been destroyed.
       允许但不要求实现让 pthread_mutex_destroy() 将非法值存储到互斥锁中。这可能有助于检测试图锁定（或以其他方式引用）已被销毁的互斥锁的错误程序。

   Tradeoff Between Error Checks and Performance Supported
   错误检查和支持的性能之间的权衡
       Many error conditions that can occur are not required to be detected by the implementation in order to let implementations trade off performance  ver‐
       sus  degree of error checking according to the needs of their specific applications and execution environment. As a general rule, conditions caused by
       the system (such as insufficient memory) are required to be detected, but conditions caused by an erroneously coded application (such  as  failing  to
       provide adequate synchronization to prevent a mutex from being deleted while in use) are specified to result in undefined behavior.
       许多可能发生的错误条件不需要由实现来检测，以便让实现根据其特定应用程序和执行环境的需要在性能与错误检查程度之间进行权衡。作为一般规则，需要检测由系统引起的状况（例如内存不足），但由错误编码的应用程序引起的状况（例如未能提供足够的同步以防止互斥体在使用时被删除）是指定导致未定义的行为。

       A  wide  range  of  implementations  is thus made possible. For example, an implementation intended for application debugging may implement all of the
       error checks, but an implementation running a single, provably correct application under very tight performance constraints in  an  embedded  computer
       might implement minimal checks. An implementation might even be provided in two versions, similar to the options that compilers provide: a full-check‐
       ing, but slower version; and a limited-checking, but faster version. To forbid this optionality would be a disservice to users.
       从而使广泛的实施成为可能。例如，用于应用程序调试的实现可能会实现所有错误检查，但在嵌入式计算机中在非常严格的性能约束下运行单个、可证明正确的应用程序的实现可能会实现最少的检查。一个实现甚至可能提供两个版本，类似于编译器提供的选项：一个完整​​检查但速度较慢的版本；和一个有限的检查，但更快的版本。禁止这种可选性将对用户不利。

       By carefully limiting the use of ``undefined behavior'' only to things that an erroneous (badly coded) application might  do,  and  by  defining  that
       resource-not-available errors are mandatory, this volume of POSIX.1‐2008 ensures that a fully-conforming application is portable across the full range
       of implementations, while not forcing all implementations to add overhead to check for numerous things that a correct program  never  does.  When  the
       behavior is undefined, no error number is specified to be returned on implementations that do detect the condition. This is because undefined behavior
       means anything can happen, which includes returning with any value (which might happen to be a valid, but different, error number). However, since the
       error  number might be useful to application developers when diagnosing problems during application development, a recommendation is made in rationale
       that implementors should return a particular error number if their implementation does detect the condition.
       通过小心地将“未定义行为”的使用限制为错误（编码错误）应用程序可能会做的事情，并且通过定义资源不可用错误是强制性的，这卷 POSIX.1-2008 确保了完全符合标准的应用程序可在所有实现范围内移植，同时不会强制所有实现增加开销以检查正确程序永远不会做的许多事情。当行为未定义时，没有指定错误号在检测到条件的实现上返回。这是因为未定义的行为意味着任何事情都可能发生，包括返回任何值（可能恰好是一个有效但不同的错误编号）。但是，由于在应用程序开发过程中诊断问题时错误编号可能对应用程序开发人员有用，因此建议的基本原理是，如果他们的实现确实检测到这种情况，则实现者应该返回特定的错误编号。

   Why No Limits are Defined
   为什么没有定义限制
       Defining symbols for the maximum number of mutexes and condition variables was considered but rejected because the number of these objects may  change
       dynamically. Furthermore, many implementations place these objects into application memory; thus, there is no explicit maximum.
       曾考虑为最大数量的互斥体和条件变量定义符号，但被拒绝，因为这些对象的数量可能会动态变化。此外，许多实现将这些对象放入应用程序内存中；因此，没有明确的最大值。

   Static Initializers for Mutexes and Condition Variables
   互斥体和条件变量的静态初始化器
       Providing  for  static  initialization of statically allocated synchronization objects allows modules with private static synchronization variables to
       avoid runtime initialization tests and overhead. Furthermore, it simplifies the coding of self-initializing modules. Such  modules  are  common  in  C
       libraries,  where  for  various reasons the design calls for self-initialization instead of requiring an explicit module initialization function to be
       called. An example use of static initialization follows.
       提供静态分配的同步对象的静态初始化允许具有私有静态同步变量的模块避免运行时初始化测试和开销。此外，它还简化了自初始化模块的编码。此类模块在 C 库中很常见，出于各种原因，设计要求自初始化而不是要求调用显式模块初始化函数。下面是静态初始化的示例使用。

       Without static initialization, a self-initializing routine foo() might look as follows:
       如果没有静态初始化，自初始化例程 foo() 可能如下所示:

           static pthread_once_t foo_once = PTHREAD_ONCE_INIT;
           static pthread_mutex_t foo_mutex;

           void foo_init()
           {
               pthread_mutex_init(&foo_mutex, NULL);
           }

           void foo()
           {
               pthread_once(&foo_once, foo_init);
               pthread_mutex_lock(&foo_mutex);
              /* Do work. */
               pthread_mutex_unlock(&foo_mutex);
           }

       With static initialization, the same routine could be coded as follows:
       使用静态初始化，相同的例程可以编码如下:

           static pthread_mutex_t foo_mutex = PTHREAD_MUTEX_INITIALIZER;

           void foo()
           {
               pthread_mutex_lock(&foo_mutex);
              /* Do work. */
               pthread_mutex_unlock(&foo_mutex);
           }

       Note that the static initialization both eliminates the need for the initialization test inside pthread_once() and the fetch of  &foo_mutex  to  learn
       the address to be passed to pthread_mutex_lock() or pthread_mutex_unlock().
       请注意，静态初始化既不需要在 pthread_once() 内部进行初始化测试，也不需要获取 &foo_mutex 以了解要传递给 pthread_mutex_lock() 或 pthread_mutex_unlock() 的地址。

       Thus,  the  C  code  written  to  initialize  static objects is simpler on all systems and is also faster on a large class of systems; those where the
       (entire) synchronization object can be stored in application memory.
       因此，用于初始化静态对象的 C 代码在所有系统上都更简单，在一大类系统上也更快；那些（整个）同步对象可以存储在应用程序内存中的地方。

       Yet the locking performance question is likely to be raised for machines that require mutexes to be allocated out of special  memory.   Such  machines
       actually  have  to  have  mutexes and possibly condition variables contain pointers to the actual hardware locks. For static initialization to work on
       such machines, pthread_mutex_lock() also has to  test  whether  or  not  the  pointer  to  the  actual  lock  has  been  allocated.  If  it  has  not,
       pthread_mutex_lock() has to initialize it before use. The reservation of such resources can be made when the program is loaded, and hence return codes
       have not been added to mutex locking and condition variable waiting to indicate failure to complete initialization.
       然而，对于需要从特殊内存中分配互斥锁的机器，可能会出现锁定性能问题。这样的机器实际上必须有互斥体，并且可能条件变量包含指向实际硬件锁的指针。要在此类机器上进行静态初始化，pthread_mutex_lock() 还必须测试指向实际锁的指针是否已分配。如果没有， pthread_mutex_lock() 必须在使用前对其进行初始化。可以在程序加载时保留此类资源，因此没有将返回代码添加到互斥锁和条件变量等待指示无法完成初始化。

       This runtime test in pthread_mutex_lock() would at first seem to be extra work; an extra test is required to see whether the pointer has been initial‐
       ized.  On  most machines this would actually be implemented as a fetch of the pointer, testing the pointer against zero, and then using the pointer if
       it has already been initialized. While the test might seem to add extra work, the extra effort of testing a register is usually  negligible  since  no
       extra  memory  references  are actually done. As more and more machines provide caches, the real expenses are memory references, not instructions exe‐
       cuted.
       pthread_mutex_lock() 中的这个运行时测试乍一看似乎是额外的工作；需要额外的测试来查看指针是否已经初始化。在大多数机器上，这实际上是通过获取指针来实现的，针对零测试指针，然后在指针已经初始化的情况下使用它。虽然测试似乎增加了额外的工作，但测试寄存器的额外工作通常可以忽略不计，因为实际上没有进行额外的内存引用。随着越来越多的机器提供缓存，真正的开销是内存引用，而不是执行的指令。

       Alternatively, depending on the machine architecture, there are often ways to eliminate all overhead in the most important case: on  the  lock  opera‐
       tions that occur after the lock has been initialized. This can be done by shifting more overhead to the less frequent operation: initialization. Since
       out-of-line mutex allocation also means that an address has to be dereferenced to find the actual lock, one technique that is widely applicable is  to
       have  static  initialization  store  a bogus value for that address; in particular, an address that causes a machine fault to occur. When such a fault
       occurs upon the first attempt to lock such a mutex, validity checks can be done, and then the correct address for the actual lock can  be  filled  in.
       Subsequent  lock  operations incur no extra overhead since they do not ``fault''. This is merely one technique that can be used to support static ini‐
       tialization, while not adversely affecting the performance of lock acquisition. No doubt there are other techniques that are highly machine-dependent.
       或者，根据机器架构，通常有一些方法可以消除最重要情况下的所有开销：在锁初始化后发生的锁操作。这可以通过将更多的开销转移到不那么频繁的操作来完成：初始化。由于外联互斥分配也意味着必须取消引用地址才能找到实际锁，因此一种广泛适用的技术是让静态初始化为该地址存储一个虚假值；特别是导致机器故障发生的地址。当第一次尝试锁定这样的互斥锁时发生这样的错误时，可以进行有效性检查，然后可以填充实际锁的正确地址。后续的锁定操作不会产生额外的开销，因为它们不会“错误” '。这只是一种可用于支持静态初始化的技术，同时不会对锁获取的性能产生不利影响。毫无疑问，还有其他高度依赖机器的技术。

       The locking overhead for machines doing out-of-line mutex allocation is thus similar for modules being implicitly initialized, where  it  is  improved
       for those doing mutex allocation entirely inline. The inline case is thus made much faster, and the out-of-line case is not significantly worse.
       因此，进行外联互斥分配的机器的锁定开销对于隐式初始化的模块是相似的，而对于完全内联进行互斥分配的机器来说，它得到了改进。因此，内联情况变得更快，而外联情况并没有明显更糟。

       Besides  the  issue of locking performance for such machines, a concern is raised that it is possible that threads would serialize contending for ini‐
       tialization locks when attempting to finish initializing statically allocated mutexes. (Such finishing would  typically  involve  taking  an  internal
       lock,  allocating  a  structure,  storing a pointer to the structure in the mutex, and releasing the internal lock.) First, many implementations would
       reduce such serialization by hashing on the mutex address. Second, such serialization can only occur a bounded number of times. In particular, it  can
       happen  at  most as many times as there are statically allocated synchronization objects. Dynamically allocated objects would still be initialized via
       pthread_mutex_init() or pthread_cond_init().
       除了此类机器的锁定性能问题外，人们还担心线程在尝试完成初始化静态分配的互斥锁时可能会序列化竞争初始化锁。 （这样的完成通常涉及获取内部锁、分配结构、在互斥锁中存储指向该结构的指针并释放内部锁。）首先，许多实现会通过对互斥锁地址进行散列来减少这种序列化。其次，这种序列化只能发生有限的次数。特别是，它最多可以发生与静态分配的同步对象一样多的次数。动态分配的对象仍将通过 pthread_mutex_init() 或 pthread_cond_init() 进行初始化。

       Finally, if none of the above optimization techniques for out-of-line allocation yields sufficient performance for an application on some  implementa‐
       tion,  the  application  can  avoid  static  initialization  altogether  by explicitly initializing all synchronization objects with the corresponding
       pthread_*_init() functions, which are supported by all implementations. An implementation can also document the tradeoffs and advise which initializa‐
       tion technique is more efficient for that particular implementation.
       最后，如果上述用于离线分配的优化技术都不能为某个实现上的应用程序产生足够的性能，则应用程序可以通过使用相应的 pthread__init() 函数显式初始化所有同步对象来完全避免静态初始化，这所有实现都支持。实现还可以记录权衡并建议哪种初始化技术对于该特定实现更有效。

   Destroying Mutexes
   销毁互斥锁
       A mutex can be destroyed immediately after it is unlocked. For example, consider the following code:
       互斥锁可以在解锁后立即销毁。例如，考虑以下代码:

           struct obj {
           pthread_mutex_t om;
               // reference count(引用计数)
               int refcnt;
               ...
           };

           obj_done(struct obj *op)
           {
               pthread_mutex_lock(&op->om);
               if (--op->refcnt == 0) {
                   pthread_mutex_unlock(&op->om);
           (A)     pthread_mutex_destroy(&op->om);
           (B)     free(op);
               } else
           (C)     pthread_mutex_unlock(&op->om);
           }

       In  this  case obj is reference counted and obj_done() is called whenever a reference to the object is dropped.  Implementations are required to allow
       an object to be destroyed and freed and potentially unmapped (for example, lines A and B) immediately after the object is unlocked (line C).
       在这种情况下， obj 被引用计数并且 obj_done() 每当对对象的引用被删除时就会被调用。实现需要允许在对象解锁后立即销毁和释放并可能取消映射（例如，行 A 和 B）（行 C）。

   Robust Mutexes
   健壮的互斥量
       Implementations are required to provide robust mutexes for mutexes with the process-shared attribute set  to  PTHREAD_PROCESS_SHARED.  Implementations
       are allowed, but not required, to provide robust mutexes when the process-shared attribute is set to PTHREAD_PROCESS_PRIVATE.
       实现需要为进程共享属性设置为 PTHREAD_PROCESS_SHARED 的互斥锁提供健壮的互斥锁。当进程共享属性设置为 PTHREAD_PROCESS_PRIVATE 时，允许但不要求实现提供健壮的互斥锁。

FUTURE DIRECTIONS
未来发展方向
       None.

SEE ALSO
       pthread_mutex_getprioceiling(), pthread_mutexattr_getrobust(), pthread_mutex_lock(), pthread_mutex_timedlock(), pthread_mutexattr_getpshared()

       The Base Definitions volume of POSIX.1‐2008, <pthread.h>

COPYRIGHT
       Portions  of this text are reprinted and reproduced in electronic form from IEEE Std 1003.1, 2013 Edition, Standard for Information Technology -- Por‐
       table Operating System Interface (POSIX), The Open Group Base Specifications Issue 7, Copyright (C) 2013 by the Institute of Electrical and  Electron‐
       ics  Engineers, Inc and The Open Group.  (This is POSIX.1-2008 with the 2013 Technical Corrigendum 1 applied.) In the event of any discrepancy between
       this version and the original IEEE and The Open Group Standard, the original IEEE and The Open Group Standard is the referee  document.  The  original
       Standard can be obtained online at http://www.unix.org/online.html .

       Any  typographical  or formatting errors that appear in this page are most likely to have been introduced during the conversion of the source files to
       man page format. To report such errors, see https://www.kernel.org/doc/man-pages/reporting_bugs.html .

IEEE/The Open Group                                                          2013                                                   PTHREAD_MUTEX_DESTROY(3P)

]]
# 互斥量是信号量的一种特殊情况。
#[[

PTHREAD_MUTEX_LOCK(3P)                                                          POSIX Programmer's Manual                                                          PTHREAD_MUTEX_LOCK(3P)

PROLOG
       This  manual  page  is  part  of the POSIX Programmer's Manual.  The Linux implementation of this interface may differ (consult the corresponding Linux manual page for details of
       Linux behavior), or the interface may not be implemented on Linux.

NAME
       pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_unlock — lock and unlock a mutex
       锁定和解锁互斥锁。
       加锁和解锁互斥锁。

SYNOPSIS
       #include <pthread.h>

       int pthread_mutex_lock(pthread_mutex_t *mutex);
       int pthread_mutex_trylock(pthread_mutex_t *mutex);
       int pthread_mutex_unlock(pthread_mutex_t *mutex);

DESCRIPTION
       The mutex object referenced by mutex shall be locked by a call to pthread_mutex_lock() that returns zero or [EOWNERDEAD].  If the mutex is already locked by another  thread,  the
       calling thread shall block until the mutex becomes available. This operation shall return with the mutex object referenced by mutex in the locked state with the calling thread as
       its owner. If a thread attempts to relock a mutex that it has already locked, pthread_mutex_lock() shall behave as described in the Relock column of the  following  table.  If  a
       thread attempts to unlock a mutex that it has not locked or a mutex which is unlocked, pthread_mutex_unlock() shall behave as described in the Unlock When Not Owner column of the
       following table.
       互斥锁引用的互斥锁对象应通过调用返回零或 [EOWNERDEAD] 的 pthread_mutex_lock() 来锁定。如果互斥锁已被另一个线程锁定，则调用线程应阻塞，直到互斥锁可用。
       此操作应返回处于锁定状态的互斥锁引用的互斥锁对象，调用线程作为其所有者。
       如果线程试图重新锁定它已经锁定的互斥锁，则 pthread_mutex_lock() 的行为应如下表的重新锁定列中所述。
       如果一个线程试图解锁一个它没有锁定的互斥锁或一个被解锁的互斥锁，pthread_mutex_unlock() 应该按照下表的 Unlock When Not Owner 列中的描述进行操作。

                                                               ┌───────────┬────────────┬────────────────┬───────────────────────┐
                                                               │Mutex Type │ Robustness │     Relock     │ Unlock When Not Owner │
                                                                互斥锁类型     稳健性           重新加锁            非所有者时解锁
                                                               ├───────────┼────────────┼────────────────┼───────────────────────┤
                                                               │NORMAL     │ non-robust │ deadlock       │ undefined behavior    │
                                                                正常          不稳健         死锁             未定义的行为
                                                               ├───────────┼────────────┼────────────────┼───────────────────────┤
                                                               │NORMAL     │ robust     │ deadlock       │ error returned        │
                                                                              稳健                               返回错误
                                                               ├───────────┼────────────┼────────────────┼───────────────────────┤
                                                               │ERRORCHECK │ either     │ error returned │ error returned        │
                                                               ├───────────┼────────────┼────────────────┼───────────────────────┤
                                                               │RECURSIVE  │ either     │ recursive      │ error returned        │
                                                               │           │            │ (see below)    │                       │
                                                               ├───────────┼────────────┼────────────────┼───────────────────────┤
                                                               │DEFAULT    │ non-robust │ undefined      │ undefined behavior†   │
                                                               │           │            │ behavior†      │                       │
                                                               ├───────────┼────────────┼────────────────┼───────────────────────┤
                                                               │DEFAULT    │ robust     │ undefined      │ error returned        │
                                                               │           │            │ behavior†      │                       │
                                                               └───────────┴────────────┴────────────────┴───────────────────────┘
       †     If the mutex type is PTHREAD_MUTEX_DEFAULT, the behavior of pthread_mutex_lock() may correspond to one of the three other standard mutex types as  described  in  the  table
             above. If it does not correspond to one of those three, the behavior is undefined for the cases marked †.
       † 如果互斥类型为 PTHREAD_MUTEX_DEFAULT，则 pthread_mutex_lock() 的行为可能对应于上表中描述的其他三种标准互斥类型之一。如果它不对应于这三个中的一个，则对于标记为 † 的情况，行为未定义。

       Where the table indicates recursive behavior, the mutex shall maintain the concept of a lock count. When a thread successfully acquires a mutex for the first time, the lock count
       shall be set to one. Every time a thread relocks this mutex, the lock count shall be incremented by one. Each time the thread unlocks the mutex, the lock count  shall  be  decre‐
       mented by one. When the lock count reaches zero, the mutex shall become available for other threads to acquire.
       在表指示递归行为的情况下，互斥锁应维护锁计数的概念。当线程第一次成功获取互斥锁时，锁计数应设置为 1。每次线程重新锁定此互斥锁时，锁定计数应加一。每次线程解锁互斥锁时，锁计数应减一。当锁计数达到零时，互斥锁将可供其他线程获取。

       The  pthread_mutex_trylock() function shall be equivalent to pthread_mutex_lock(), except that if the mutex object referenced by mutex is currently locked (by any thread, includ‐
       ing the current thread), the call shall return immediately. If the mutex type is PTHREAD_MUTEX_RECURSIVE and the mutex is currently owned by the calling thread,  the  mutex  lock
       count shall be incremented by one and the pthread_mutex_trylock() function shall immediately return success.
       pthread_mutex_trylock() 函数应等效于 pthread_mutex_lock()，但如果 mutex 引用的互斥对象当前被锁定（被任何线程，包括当前线程），则调用应立即返回。如果互斥类型为 PTHREAD_MUTEX_RECURSIVE 并且互斥当前由调用线程拥有，则互斥锁计数应增加 1，pthread_mutex_trylock() 函数应立即返回成功。

       The  pthread_mutex_unlock() function shall release the mutex object referenced by mutex.  The manner in which a mutex is released is dependent upon the mutex's type attribute. If
       there are threads blocked on the mutex object referenced by mutex when pthread_mutex_unlock() is called, resulting in the mutex becoming available, the  scheduling  policy  shall
       determine which thread shall acquire the mutex.
       pthread_mutex_unlock() 函数应释放互斥锁引用的互斥锁对象。释放互斥锁的方式取决于互斥锁的类型属性。如果在调用 pthread_mutex_unlock() 时 mutex 引用的 mutex 对象上有线程阻塞，导致 mutex 可用，则调度策略将确定哪个线程应获取 mutex。

       (In the case of PTHREAD_MUTEX_RECURSIVE mutexes, the mutex shall become available when the count reaches zero and the calling thread no longer has any locks on this mutex.)
       （在 PTHREAD_MUTEX_RECURSIVE 互斥锁的情况下，当计数达到零并且调用线程不再对此互斥锁有任何锁定时，互斥锁将变为可用。）

       If a signal is delivered to a thread waiting for a mutex, upon return from the signal handler the thread shall resume waiting for the mutex as if it was not interrupted.
       如果将信号传递给等待互斥锁的线程，则在从信号处理程序返回时，线程将继续等待互斥锁，就好像它没有被中断一样。

       If  mutex  is  a  robust  mutex  and the process containing the owning thread terminated while holding the mutex lock, a call to pthread_mutex_lock() shall return the error value
       [EOWNERDEAD].  If mutex is a robust mutex and the owning thread terminated while holding the mutex lock, a call to pthread_mutex_lock() may return the  error  value  [EOWNERDEAD]
       even  if the process in which the owning thread resides has not terminated. In these cases, the mutex is locked by the thread but the state it protects is marked as inconsistent.
       The application should ensure that the state is made consistent for reuse and when that is complete call pthread_mutex_consistent().  If the application is unable to recover  the
       state, it should unlock the mutex without a prior call to pthread_mutex_consistent(), after which the mutex is marked permanently unusable.
       如果互斥锁是一个健壮的互斥锁并且包含拥有线程的进程在持有互斥锁时终止，则调用 pthread_mutex_lock() 将返回错误值 [EOWNERDEAD]。如果互斥体是健壮的互斥体并且拥有互斥锁的拥有线程终止，则调用 pthread_mutex_lock() 可能会返回错误值 [EOWNERDEAD]，即使拥有线程所在的进程尚未终止。在这些情况下，互斥锁被线程锁定，但它保护的状态被标记为不一致。应用程序应确保状态一致以供重用，并在完成时调用 pthread_mutex_consistent()。如果应用程序无法恢复状态，它应该在没有事先调用 pthread_mutex_consistent() 的情况下解锁互斥锁，之后互斥锁被标记为永久不可用。

       If mutex does not refer to an initialized mutex object, the behavior of pthread_mutex_lock(), pthread_mutex_trylock(), and pthread_mutex_unlock() is undefined.
       如果互斥锁不引用已初始化的互斥锁对象，则 pthread_mutex_lock()、pthread_mutex_trylock() 和 pthread_mutex_unlock() 的行为未定义。

RETURN VALUE
       If successful, the pthread_mutex_lock(), pthread_mutex_trylock(), and pthread_mutex_unlock() functions shall return zero; otherwise, an error number shall be returned to indicate
       the error.
       如果成功，pthread_mutex_lock()、pthread_mutex_trylock() 和 pthread_mutex_unlock() 函数应返回零；否则，将返回错误编号以指示错误。

ERRORS
       The pthread_mutex_lock() and pthread_mutex_trylock() functions shall fail if:
       如果出现以下情况，pthread_mutex_lock() 和 pthread_mutex_trylock() 函数将失败:

       EAGAIN The mutex could not be acquired because the maximum number of recursive locks for mutex has been exceeded.
       无法获取互斥锁，因为已超过互斥锁的最大递归锁数。

       EINVAL The mutex was created with the protocol attribute having the value PTHREAD_PRIO_PROTECT and the calling thread's priority is higher than the mutex's current priority ceil‐
              ing.
       互斥锁是使用具有值 PTHREAD_PRIO_PROTECT 的协议属性创建的，并且调用线程的优先级高于互斥锁的当前优先级上限。

       ENOTRECOVERABLE
       不可恢复
              The state protected by the mutex is not recoverable.
              互斥锁保护的状态是不可恢复的。

       EOWNERDEAD
              The  mutex is a robust mutex and the process containing the previous owning thread terminated while holding the mutex lock. The mutex lock shall be acquired by the calling
              thread and it is up to the new owner to make the state consistent.
              互斥锁是一个健壮的互斥锁，包含前一个拥有线程的进程在持有互斥锁时终止。互斥锁应由调用线程获取，由新所有者来使状态保持一致。

       The pthread_mutex_lock() function shall fail if:
       如果出现以下情况，pthread_mutex_lock() 函数将失败:

       EDEADLK
              The mutex type is PTHREAD_MUTEX_ERRORCHECK and the current thread already owns the mutex.

       The pthread_mutex_trylock() function shall fail if:

       EBUSY  The mutex could not be acquired because it was already locked.

       The pthread_mutex_unlock() function shall fail if:

       EPERM  The mutex type is PTHREAD_MUTEX_ERRORCHECK or PTHREAD_MUTEX_RECURSIVE, or the mutex is a robust mutex, and the current thread does not own the mutex.

       The pthread_mutex_lock() and pthread_mutex_trylock() functions may fail if:

       EOWNERDEAD
              The mutex is a robust mutex and the previous owning thread terminated while holding the mutex lock. The mutex lock shall be acquired by the calling thread and it is up  to
              the new owner to make the state consistent.

       The pthread_mutex_lock() function may fail if:

       EDEADLK
              A deadlock condition was detected.

       These functions shall not return an error code of [EINTR].

       The following sections are informative.

EXAMPLES
       None.

APPLICATION USAGE
程序用法
       Applications that have assumed that non-zero return values are errors will need updating for use with robust mutexes, since a valid return for a thread acquiring a mutex which is
       protecting a currently inconsistent state is [EOWNERDEAD].  Applications that do not check the error returns, due to ruling out the possibility of such errors arising, should not
       use  robust mutexes. If an application is supposed to work with normal and robust mutexes it should check all return values for error conditions and if necessary take appropriate
       action.
       假设非零返回值是错误的应用程序将需要更新以与强大的互斥锁一起使用，因为获取保护当前不一致状态的互斥锁的线程的有效返回是 [EOWNERDEAD]。不检查错误返回的应用程序，由于排除了出现此类错误的可能性，不应使用健壮的互斥锁。如果应用程序应该使用正常和健壮的互斥锁，它应该检查所有返回值的错误情况，并在必要时采取适当的措施。

RATIONALE
基本原理
       Mutex objects are intended to serve as a low-level primitive from which other thread synchronization functions can be built. As such, the implementation of mutexes should  be  as
       efficient as possible, and this has ramifications on the features available at the interface.
       互斥对象旨在用作可以构建其他线程同步函数的低级原语。因此，互斥体的实现应该尽可能高效，这会对接口上可用的功能产生影响。

       The  mutex  functions and the particular default settings of the mutex attributes have been motivated by the desire to not preclude fast, inlined implementations of mutex locking
       and unlocking.
       互斥函数和互斥属性的特定默认设置的动机是希望不排除互斥锁定和解锁的快速、内联实现。

       Since most attributes only need to be checked when a thread is going to be blocked, the use of attributes does not slow the (common) mutex-locking case.
       由于大多数属性只需要在线程将被阻塞时才需要检查，因此使用属性不会减慢（常见）互斥锁情况。

       Likewise, while being able to extract the thread ID of the owner of a mutex might be desirable, it would require storing the current thread ID when each mutex is locked, and this
       could incur unacceptable levels of overhead. Similar arguments apply to a mutex_tryunlock operation.
       同样，虽然能够提取互斥锁所有者的线程 ID 可能是可取的，但在每个互斥锁被锁定时需要存储当前线程 ID，这可能会导致无法接受的开销级别。类似的参数适用于 mutex_tryunlock 操作。

       For further rationale on the extended mutex types, see the Rationale (Informative) volume of POSIX.1‐2008, Threads Extensions.
       有关扩展互斥锁类型的更多基本原理，请参阅 POSIX.1-2008 的基本原理（信息性）卷，线程扩展。

       If  an  implementation  detects  that the value specified by the mutex argument does not refer to an initialized mutex object, it is recommended that the function should fail and
       report an [EINVAL] error.
       如果实现检测到 mutex 参数指定的值不引用已初始化的 mutex 对象，则建议该函数应失败并报告 [EINVAL] 错误。

FUTURE DIRECTIONS
       None.

SEE ALSO
       pthread_mutex_consistent(), pthread_mutex_destroy(), pthread_mutex_timedlock(), pthread_mutexattr_getrobust()

       The Base Definitions volume of POSIX.1‐2008, Section 4.11, Memory Synchronization, <pthread.h>

COPYRIGHT
       Portions of this text are reprinted and reproduced in electronic form from IEEE Std 1003.1, 2013 Edition, Standard for Information Technology -- Portable Operating System  Inter‐
       face  (POSIX),  The  Open  Group  Base  Specifications  Issue  7,  Copyright  (C) 2013 by the Institute of Electrical and Electronics Engineers, Inc and The Open Group.  (This is
       POSIX.1-2008 with the 2013 Technical Corrigendum 1 applied.) In the event of any discrepancy between this version and the original IEEE and The Open Group Standard, the  original
       IEEE and The Open Group Standard is the referee document. The original Standard can be obtained online at http://www.unix.org/online.html .

       Any  typographical  or  formatting errors that appear in this page are most likely to have been introduced during the conversion of the source files to man page format. To report
       such errors, see https://www.kernel.org/doc/man-pages/reporting_bugs.html .

IEEE/The Open Group                                                                        2013                                                                    PTHREAD_MUTEX_LOCK(3P)

]]
#[[
注意: Ubuntu 20 是默认没有 pthread_mutex_init(); pthread_mutex_destroy();等函数的man手册的,
而 CentOS是有pthread线程库的man手册的。
所以在 Ubuntu上 sudo apt-get install manpages-posix-dev, 安装就可以了。
]]


#[[
在程序中加入互斥量机制。
并且分析程序到底是哪里造成了竞争?
]]
add_executable(thread-fileadd2 thread-fileadd2.cpp)
#[[
2 版本进行了分析, 没有进行改进, 之后复习一定要看这个分析的过程, 非常重要！
]]


#[[
3 版本对 2 版本的分析, 进行实际的代码的改进。
]]
add_executable(thread-fileadd3 thread-fileadd3.cpp)

#[[
3 版本运行段错误, 不知道是哪的原因? 通过调试执行第一次 pthread_create();后, 就出现了段错误。
心态崩了。

所以有了下面这个 4 版本, 先把 lambda表达式 删掉, 使用最直接的方式实现这个功能后再重构。
]]
add_executable(thread-fileadd4 thread-fileadd4.cpp)

#[[
todo 笔记:在 4 版本中终于找到了问题, 那就是 pthread_cleanup_pop(1); 写的位置不对。
 pthread_cleanup_pop(1);
 正确的版本看 5 版本。
]]
add_executable(thread-fileadd5 thread-fileadd5.cpp)

#[[
5 版本已经是正确的版本了, 6 版本是使用 lambda表达式的版本。
]]
add_executable(thread-fileadd6 thread-fileadd6.cpp)

#[[
互斥量的基本用法在 6 版本中体现了。
]]


#[[
mst: 有4个线程, 这四个线程往终端上拼命的输出abcd。
        第一个线程拼命的打印a,
        第二个线程拼命的打印b,
        第三个线程拼命的打印c,
        第四个线程拼命的打印d。
     我希望看到工工整整的结果: abcdabcdabcd...。
]]
add_executable(thread-abcd1 thread-abcd1.cpp)
#[[
使用锁链, 即线程1执行了后去解锁线程2, 线程2执行了后去解锁线程3, 线程3执行了后去解锁线程4,
线程4执行了后去解锁线程1。
这里其实是妙用死锁。
]]
add_executable(thread-abcd2 thread-abcd2.cpp)

#[[
使用池类算法来计算质数。这里没有使用交叉分配法。
]]
add_executable(thread-pool-primer1 thread-pool-primer1.cpp)
#[[
2 版本成功的设计了出来。
但是这个版本是盲等版本。具体的分析查看源码中的注释。
]]
add_executable(thread-pool-primer2 thread-pool-primer2.cpp)


#[[
将之前使用信号机制做出来的令牌桶改为多线程并发版本。
1. 这个版本是盲等的版本, 因为还没有复习到条件变量这个通知原语。
2. 这个版本是使用了一个静态全局变量 isLoop 来使 alarm线程正常停下来。
3. 这个版本成功编译运行, 而且正常退出, 进程退出码为0。没有内存泄漏。
4. 但是这个版本没有写完, 因为 tbfInit();函数如果是在多个线程中都使用到了就会出错。
   那为什么 3. 可以正常运行呢? 因为 3. 是在 main线程 中使用了一个令牌桶而已,
   所以不会出错。
]]
add_subdirectory(mytbf_multithread)

#[[
这个版本使用 pthread_cancel();来取消 alarm线程。
这个版本仅仅是使用 pthread_cancel();来代替 isLoop 用来取消 alarm线程。
测试运行的结果也是正确的。
这个版本成功编译运行, 而且正常退出, 进程退出码为0。没有内存泄漏。

这个版本较上一个版本改动非常小, 就是使用 pthread_cancel();来代替 isLoop 用来取消 alarm线程。
而且没有问题。

]]
add_subdirectory(mytbf_multithread2)


#[[
这个 3 版本在 2 版本的基础上继续完善这个多线程下的令牌桶设计。

问题在模块加载那里。

在模块加载那里加锁保护 inited 变量即可。
而 3 这个版本我也这么做了, 编译运行没有问题。
至此就设计成功了。多线程的令牌桶库。
]]
add_subdirectory(mytbf_multithread3)

#[[
而这里还有另外一种办法: 使用 pthread_once();函数。

PTHREAD_ONCE(3POSIX)                                                            POSIX Programmer's Manual                                                            PTHREAD_ONCE(3POSIX)

PROLOG
This  manual  page  is  part  of the POSIX Programmer's Manual.  The Linux implementation of this interface may differ (consult the corresponding Linux manual page for details of
Linux behavior), or the interface may not be implemented on Linux.

NAME
        pthread_once — dynamic package initialization
        动态包初始化。
        动态模块的单次初始化函数。

SYNOPSIS
        #include <pthread.h>

        // pthread_once();实现某个模块的单次初始化。
        // 这个函数需要一个变量 pthread_once_t once_control = PTHREAD_ONCE_INIT;
        // 第二个参数为一个函数指针。
        //
        // pthread_once();保证用户给它的函数只被调用一次。
        其实就相当于
            pthread_mutex_lock( &MUTEX_MODULE_LOAD_INITED );
            if (!inited) {
                moduleLoad();
                inited = 1;
            }
            pthread_mutex_unlock( &MUTEX_MODULE_LOAD_INITED );
        这些内容。



        int pthread_once(pthread_once_t *once_control,
        void (*init_routine)(void));
        pthread_once_t once_control = PTHREAD_ONCE_INIT;

DESCRIPTION
        The first call to pthread_once() by any thread in a process, with a given once_control, shall call the init_routine with no arguments. Subsequent calls of pthread_once() with the
        same once_control shall not call the init_routine.  On return from pthread_once(), init_routine shall have completed. The once_control parameter shall determine whether the asso‐
        ciated initialization routine has been called.
        进程中的任何线程对 pthread_once() 的第一次调用，使用给定的 once_control，将调用 init_routine 不带参数。
        具有相同 once_control 的 pthread_once() 的后续调用不应调用 init_routine。
        从 pthread_once() 返回时，init_routine 应已完成。 once_control 参数应确定相关的初始化例程是否已被调用。

        The pthread_once() function is not a cancellation point. However, if init_routine is  a  cancellation  point  and  is  canceled,  the  effect  on  once_control  shall  be  as  if
        pthread_once() was never called.
        pthread_once() 函数不是取消点。但是，如果 init_routine 是取消点并被取消，则对 once_control 的影响就好像从未调用过 pthread_once() 一样。

        The constant PTHREAD_ONCE_INIT is defined in the <pthread.h> header.
        常量 PTHREAD_ONCE_INIT 在 <pthread.h> 头文件中定义。

        The behavior of pthread_once() is undefined if once_control has automatic storage duration or is not initialized by PTHREAD_ONCE_INIT.
        如果 once_control 具有自动存储持续时间或未由 PTHREAD_ONCE_INIT 初始化，则 pthread_once() 的行为未定义。

RETURN VALUE
        Upon successful completion, pthread_once() shall return zero; otherwise, an error number shall be returned to indicate the error.
        成功完成后，pthread_once() 应返回零；否则，将返回错误编号以指示错误。

ERRORS
        The pthread_once() function shall not return an error code of [EINTR].
        pthread_once() 函数不应返回 [EINTR] 的错误代码。

        The following sections are informative.
        以下部分是信息性的。

EXAMPLES
None.

APPLICATION USAGE
None.

RATIONALE
rationale
基本原理
        Some  C libraries are designed for dynamic initialization. That is, the global initialization for the library is performed when the first procedure in the library is called. In a
        single-threaded program, this is normally implemented using a static variable whose value is checked on entry to a routine, as follows:
        一些 C 库是为动态初始化而设计的。即在调用库中的第一个过程时执行库的全局初始化。
        在单线程程序中，这通常使用静态变量实现，其值在进入例程时检查，如下所示:

        static int random_is_initialized = 0;
        extern int initialize_random();

        int random_function()
        {
            if (random_is_initialized == 0) {
                initialize_random();
                random_is_initialized = 1;
            }
        ... /* Operations performed after initialization. */
        }

        To keep the same structure in a multi-threaded program, a new primitive is needed. Otherwise, library initialization has to be accomplished by an explicit call to  a  library-ex‐
        ported initialization function prior to any use of the library.
        为了在多线程程序中保持相同的结构，需要一个新的原语。否则，在使用库之前，必须通过显式调用库导出的初始化函数来完成库初始化。

        For  dynamic  library  initialization in a multi-threaded process, a simple initialization flag is not sufficient; the flag needs to be protected against modification by multiple
        threads simultaneously calling into the library. Protecting the flag requires the use of a mutex; however, mutexes have to be initialized before they are used.  Ensuring that the
        mutex is only initialized once requires a recursive solution to this problem.
        对于多线程进程中的动态库初始化，简单的初始化标志是不够的；需要保护标志不被多个线程同时调用库修改。保护标志需要使用互斥锁；但是，互斥体在使用之前必须被初始化。确保互斥锁只初始化一次需要递归解决这个问题。

        The  use  of  pthread_once() not only supplies an implementation-guaranteed means of dynamic initialization, it provides an aid to the reliable construction of multi-threaded and
        realtime systems. The preceding example then becomes:
        pthread_once() 的使用不仅提供了一种保证实现的动态初始化方法，它还为多线程和实时系统的可靠构建提供了帮助。前面的例子就变成了:

        #include <pthread.h>
        static pthread_once_t random_is_initialized = PTHREAD_ONCE_INIT;
        extern int initialize_random();

        int random_function()
        {
            (void) pthread_once(&random_is_initialized, initialize_random);
        ... /* Operations performed after initialization. */
        }

        Note that a pthread_once_t cannot be an array because some compilers do not accept the construct &<array_name>.
        请注意，pthread_once_t 不能是数组，因为某些编译器不接受结构 &<array_name>。

        If an implementation detects that the value specified by the once_control argument to pthread_once() does not refer to a pthread_once_t object initialized  by  PTHREAD_ONCE_INIT,
        it is recommended that the function should fail and report an [EINVAL] error.
        如果实现检测到 pthread_once() 的 once_control 参数指定的值不引用由 PTHREAD_ONCE_INIT 初始化的 pthread_once_t 对象，则建议该函数应失败并报告 [EINVAL] 错误。

FUTURE DIRECTIONS
None.

SEE ALSO
    The Base Definitions volume of POSIX.1‐2008, <pthread.h>

COPYRIGHT
Portions  of this text are reprinted and reproduced in electronic form from IEEE Std 1003.1, 2013 Edition, Standard for Information Technology -- Portable Operating System Inter‐
face (POSIX), The Open Group Base Specifications Issue 7, Copyright (C) 2013 by the Institute of Electrical  and  Electronics  Engineers,  Inc  and  The  Open  Group.   (This  is
POSIX.1-2008  with the 2013 Technical Corrigendum 1 applied.) In the event of any discrepancy between this version and the original IEEE and The Open Group Standard, the original
IEEE and The Open Group Standard is the referee document. The original Standard can be obtained online at http://www.unix.org/online.html .

Any typographical or formatting errors that appear in this page are most likely to have been introduced during the conversion of the source files to man page  format.  To  report
such errors, see https://www.kernel.org/doc/man-pages/reporting_bugs.html .

IEEE/The Open Group                                                                        2013                                                                      PTHREAD_ONCE(3POSIX)

]]

#[[
4 版本使用 pthread_once();重构 3 版本。

运行成功, 没有问题。

但是, 这个令牌桶还是有缺陷, 那就是盲等。CPU的使用率会飙升。
盲等在, 我在 fetchToken();函数中不停的查看 token的值。

加入条件变量这种通知原语后, 程序就不会出现盲等现象了。

]]
add_subdirectory(mytbf_multithread4)

#[[
5 版本是删除了各种注释。
]]
add_subdirectory(mytbf_multithread5)


#[[
使用条件变量(condition variable)将查询法转换为通知法。

pthread_cond_t

pthread_cond_init();
pthread_cond_destroy();

PTHREAD_COND_DESTROY(3POSIX)                                                    POSIX Programmer's Manual                                                    PTHREAD_COND_DESTROY(3POSIX)

PROLOG
       This  manual  page  is  part  of the POSIX Programmer's Manual.  The Linux implementation of this interface may differ (consult the corresponding Linux manual page for details of
       Linux behavior), or the interface may not be implemented on Linux.

NAME
       pthread_cond_destroy, pthread_cond_init — destroy and initialize condition variables
       销毁和初始化条件变量。

SYNOPSIS
       #include <pthread.h>

       int pthread_cond_destroy(pthread_cond_t *cond);

       // 初始化和 互斥量一样提供了两种: 静态初始化和动态初始化。

       int pthread_cond_init(pthread_cond_t *restrict cond,
           const pthread_condattr_t *restrict attr);

       // 静态初始化按照默认的条件变量初始化属性。

       pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

DESCRIPTION
       The pthread_cond_destroy() function shall destroy the given condition variable specified by cond; the object becomes,  in  effect,  uninitialized.  An  implementation  may  cause
       pthread_cond_destroy() to set the object referenced by cond to an invalid value. A destroyed condition variable object can be reinitialized using pthread_cond_init(); the results
       of otherwise referencing the object after it has been destroyed are undefined.
       pthread_cond_destroy() 函数应销毁由 cond 指定的给定条件变量；该对象实际上未初始化。实现可能会导致 pthread_cond_destroy() 将 cond 引用的对象设置为无效值。被破坏的条件变量对象可以使用 pthread_cond_init() 重新初始化；在对象被销毁后以其他方式引用对象的结果是未定义的。

       It shall be safe to destroy an initialized condition variable upon which no threads are currently blocked. Attempting to destroy a condition variable upon which other threads are
       currently blocked results in undefined behavior.
       销毁当前没有阻塞线程的初始化条件变量应该是安全的。尝试销毁当前阻塞其他线程的条件变量会导致未定义的行为。

       The  pthread_cond_init()  function shall initialize the condition variable referenced by cond with attributes referenced by attr.  If attr is NULL, the default condition variable
       attributes shall be used; the effect is the same as passing the address of a default condition variable attributes object. Upon successful initialization, the state of the condi‐
       tion variable shall become initialized.
       pthread_cond_init() 函数应该用attr 引用的属性初始化cond 引用的条件变量。如果 attr 为 NULL，则应使用默认条件变量属性；效果与传递默认条件变量属性对象的地址相同。成功初始化后，条件变量的状态将变为初始化状态。

       Only  cond  itself  may  be  used  for  performing  synchronization.  The  result  of  referring  to  copies  of  cond  in calls to pthread_cond_wait(), pthread_cond_timedwait(),
       pthread_cond_signal(), pthread_cond_broadcast(), and pthread_cond_destroy() is undefined.
       只有 cond 本身可以用于执行同步。在对 pthread_cond_wait()、pthread_cond_timedwait()、pthread_cond_signal()、pthread_cond_broadcast() 和 pthread_cond_destroy() 的调用中引用 cond 副本的结果未定义。

       Attempting to initialize an already initialized condition variable results in undefined behavior.
       尝试初始化已初始化的条件变量会导致未定义的行为。

       In cases where default condition variable attributes are appropriate, the macro PTHREAD_COND_INITIALIZER can be used to initialize condition variables. The effect shall be equiv‐
       alent to dynamic initialization by a call to pthread_cond_init() with parameter attr specified as NULL, except that no error checks are performed.
       在默认条件变量属性合适的情况下，宏 PTHREAD_COND_INITIALIZER 可用于初始化条件变量。通过调用 pthread_cond_init() 并将参数 attr 指定为 NULL，效果应等同于动态初始化，只是不执行错误检查。

       The behavior is undefined if the value specified by the cond argument to pthread_cond_destroy() does not refer to an initialized condition variable.
       如果 pthread_cond_destroy() 的 cond 参数指定的值不引用初始化的条件变量，则行为未定义。

       The behavior is undefined if the value specified by the attr argument to pthread_cond_init() does not refer to an initialized condition variable attributes object.
       如果 pthread_cond_init() 的 attr 参数指定的值不引用初始化的条件变量属性对象，则行为未定义。

RETURN VALUE
       If successful, the pthread_cond_destroy() and pthread_cond_init() functions shall return zero; otherwise, an error number shall be returned to indicate the error.
       如果成功，pthread_cond_destroy() 和 pthread_cond_init() 函数应返回零；否则，将返回错误编号以指示错误。

ERRORS
       The pthread_cond_init() function shall fail if:
       如果出现以下情况，pthread_cond_init() 函数将失败：

       EAGAIN The system lacked the necessary resources (other than memory) to initialize another condition variable.
       系统缺乏必要的资源（内存除外）来初始化另一个条件变量。

       ENOMEM Insufficient memory exists to initialize the condition variable.
       内存不足，无法初始化条件变量。

       These functions shall not return an error code of [EINTR].
       这些函数不应返回 [EINTR] 的错误代码。

       The following sections are informative.
       以下部分是信息性的。

APPLICATION USAGE
       None.

RATIONALE
基本原理
       If an implementation detects that the value specified by the cond argument to pthread_cond_destroy() does not refer to an initialized condition variable, it is  recommended  that
       the function should fail and report an [EINVAL] error.
       如果实现检测到 pthread_cond_destroy() 的 cond 参数指定的值未引用已初始化的条件变量，则建议该函数应失败并报告 [EINVAL] 错误。

       If an implementation detects that the value specified by the cond argument to pthread_cond_destroy() or pthread_cond_init() refers to a condition variable that is in use (for ex‐
       ample, in a pthread_cond_wait() call) by another thread, or detects that the value specified by the cond argument to pthread_cond_init() refers to an already  initialized  condi‐
       tion variable, it is recommended that the function should fail and report an [EBUSY] error.
       如果一个实现检测到 pthread_cond_destroy() 或 pthread_cond_init() 的 cond 参数指定的值引用了另一个线程正在使用的条件变量（例如，在 pthread_cond_wait() 调用中），或者检测到pthread_cond_init() 的 cond 参数指定的值指的是一个已经初始化的条件变量，建议函数应该失败并报告 [EBUSY] 错误。

       If  an  implementation  detects  that the value specified by the attr argument to pthread_cond_init() does not refer to an initialized condition variable attributes object, it is
       recommended that the function should fail and report an [EINVAL] error.
       如果实现检测到 pthread_cond_init() 的 attr 参数指定的值未引用已初始化的条件变量属性对象，则建议该函数应失败并报告 [EINVAL] 错误。

       See also pthread_mutex_destroy().
       另请参见 pthread_mutex_destroy()。

FUTURE DIRECTIONS
       None.

SEE ALSO
       pthread_cond_broadcast(), pthread_cond_timedwait(), pthread_mutex_destroy()

       The Base Definitions volume of POSIX.1‐2008, <pthread.h>

COPYRIGHT
       Portions of this text are reprinted and reproduced in electronic form from IEEE Std 1003.1, 2013 Edition, Standard for Information Technology -- Portable Operating System  Inter‐
       face  (POSIX),  The  Open  Group  Base  Specifications  Issue  7,  Copyright  (C) 2013 by the Institute of Electrical and Electronics Engineers, Inc and The Open Group.  (This is
       POSIX.1-2008 with the 2013 Technical Corrigendum 1 applied.) In the event of any discrepancy between this version and the original IEEE and The Open Group Standard, the  original
       IEEE and The Open Group Standard is the referee document. The original Standard can be obtained online at http://www.unix.org/online.html .

       Any  typographical  or  formatting errors that appear in this page are most likely to have been introduced during the conversion of the source files to man page format. To report
       such errors, see https://www.kernel.org/doc/man-pages/reporting_bugs.html .

IEEE/The Open Group                                                                        2013                                                              PTHREAD_COND_DESTROY(3POSIX)



那么如何使用条件变量来发送消息呢?
使用
pthread_cond_broadcast();
pthread_cond_signal();
这两个函数。


PTHREAD_COND_BROADCAST(3POSIX)                                                  POSIX Programmer's Manual                                                  PTHREAD_COND_BROADCAST(3POSIX)

PROLOG
       This  manual  page  is  part  of the POSIX Programmer's Manual.  The Linux implementation of this interface may differ (consult the corresponding Linux manual page for details of
       Linux behavior), or the interface may not be implemented on Linux.

NAME
       pthread_cond_broadcast, pthread_cond_signal — broadcast or signal a condition
       广播一个条件变量 或者 信号一个条件变量。

SYNOPSIS
       #include <pthread.h>

       // broadcast();把所有的等待都唤醒。

       int pthread_cond_broadcast(pthread_cond_t *cond);

       // signal();是唤醒任意一个等待, 至于唤醒哪个, 我也不知道。

       int pthread_cond_signal(pthread_cond_t *cond);

DESCRIPTION
       These functions shall unblock threads blocked on a condition variable.
       这些函数应解除阻塞在条件变量上的线程。

       The pthread_cond_broadcast() function shall unblock all threads currently blocked on the specified condition variable cond.
       pthread_cond_broadcast() 函数应解除当前在指定条件变量 cond 上阻塞的所有线程的阻塞。

       The pthread_cond_signal() function shall unblock at least one of the threads that are blocked on the specified condition variable cond (if any threads are blocked on cond).
       pthread_cond_signal() 函数应至少解除在指定条件变量 cond 上阻塞的线程之一（如果有任何线程在 cond 上阻塞）。

       If more than one thread is blocked on a condition variable, the scheduling policy shall determine the order in which threads are unblocked. When each thread unblocked as a result
       of  a  pthread_cond_broadcast()  or  pthread_cond_signal()  returns from its call to pthread_cond_wait() or pthread_cond_timedwait(), the thread shall own the mutex with which it
       called pthread_cond_wait() or pthread_cond_timedwait().  The thread(s) that are unblocked shall contend for the mutex according to the scheduling policy (if applicable),  and  as
       if each had called pthread_mutex_lock().
       如果在一个条件变量上有多个线程被阻塞，则调度策略应确定线程被解除阻塞的顺序。当每个线程因 pthread_cond_broadcast() 或 pthread_cond_signal() 从其对 pthread_cond_wait() 或 pthread_cond_timedwait() 的调用返回而解除阻塞时，该线程应拥有用于调用 pthread_cond_wait() 或 pthread_cond_timedwait() 的互斥锁。未阻塞的线程应根据调度策略（如果适用）竞争互斥锁，并且好像每个线程都调用了 pthread_mutex_lock()。

       The  pthread_cond_broadcast()  or pthread_cond_signal() functions may be called by a thread whether or not it currently owns the mutex that threads calling pthread_cond_wait() or
       pthread_cond_timedwait() have associated with the condition variable during their waits; however, if predictable scheduling behavior is required, then that mutex shall be  locked
       by the thread calling pthread_cond_broadcast() or pthread_cond_signal().
       线程可以调用 pthread_cond_broadcast() 或 pthread_cond_signal() 函数，无论它当前是否拥有调用 pthread_cond_wait() 或 pthread_cond_timedwait() 的线程在等待期间与条件变量关联的互斥锁；但是，如果需要可预测的调度行为，则该互斥锁应由调用 pthread_cond_broadcast() 或 pthread_cond_signal() 的线程锁定。

       The pthread_cond_broadcast() and pthread_cond_signal() functions shall have no effect if there are no threads currently blocked on cond.
       如果当前没有线程阻塞在 cond 上，则 pthread_cond_broadcast() 和 pthread_cond_signal() 函数将不起作用。

       The behavior is undefined if the value specified by the cond argument to pthread_cond_broadcast() or pthread_cond_signal() does not refer to an initialized condition variable.
       如果 pthread_cond_broadcast() 或 pthread_cond_signal() 的 cond 参数指定的值未引用已初始化的条件变量，则行为未定义。

RETURN VALUE
       If successful, the pthread_cond_broadcast() and pthread_cond_signal() functions shall return zero; otherwise, an error number shall be returned to indicate the error.
       如果成功，pthread_cond_broadcast() 和 pthread_cond_signal() 函数应返回零；否则，将返回错误编号以指示错误。

ERRORS
       These functions shall not return an error code of [EINTR].
       这些函数不应返回 [EINTR] 的错误代码。

       The following sections are informative.
       以下部分是信息性的。

EXAMPLES
       None.

APPLICATION USAGE
应用程序用法。
       The  pthread_cond_broadcast() function is used whenever the shared-variable state has been changed in a way that more than one thread can proceed with its task. Consider a single
       producer/multiple consumer problem, where the producer can insert  multiple  items  on  a  list  that  is  accessed  one  item  at  a  time  by  the  consumers.  By  calling  the
       pthread_cond_broadcast()  function, the producer would notify all consumers that might be waiting, and thereby the application would receive more throughput on a multi-processor.
       In addition, pthread_cond_broadcast() makes it easier to implement a read-write lock. The pthread_cond_broadcast() function is needed in order to wake up all waiting readers when
       a writer releases its lock. Finally, the two-phase commit algorithm can use this broadcast function to notify all clients of an impending transaction commit.
       pthread_cond_broadcast() 函数在共享变量状态以一种以上的线程可以继续执行其任务的方式发生更改时使用。考虑单个生产者多个消费者的问题，其中生产者可以在消费者一次访问一个项目的列表中插入多个项目。通过调用 pthread_cond_broadcast() 函数，生产者将通知所有可能正在等待的消费者，从而应用程序将在多处理器上获得更多吞吐量。此外，pthread_cond_broadcast() 可以更轻松地实现读写锁。需要 pthread_cond_broadcast() 函数以便在写入器释放其锁时唤醒所有等待的读取器。最后，两阶段提交算法可以使用这个广播函数来通知所有客户端即将发生的事务提交。

       It is not safe to use the pthread_cond_signal() function in a signal handler that is invoked asynchronously. Even if it were safe, there would still be a race between the test of
       the Boolean pthread_cond_wait() that could not be efficiently eliminated.
       在异步调用的信号处理程序中使用 pthread_cond_signal() 函数是不安全的。即使它是安全的，仍然会存在无法有效消除的布尔 pthread_cond_wait() 测试之间的竞争。

       Mutexes and condition variables are thus not suitable for releasing a waiting thread by signaling from code running in a signal handler.
       因此，互斥体和条件变量不适合通过从信号处理程序中运行的代码发出信号来释放等待线程。

RATIONALE
基本原理
       If an implementation detects that the value specified by the cond argument to pthread_cond_broadcast() or pthread_cond_signal() does not refer to an initialized  condition  vari‐
       able, it is recommended that the function should fail and report an [EINVAL] error.
       如果实现检测到 pthread_cond_broadcast() 或 pthread_cond_signal() 的 cond 参数指定的值未引用初始化条件变量，则建议该函数应失败并报告 [EINVAL] 错误。

   Multiple Awakenings by Condition Signal
   条件信号多次唤醒
       On a multi-processor, it may be impossible for an implementation of pthread_cond_signal() to avoid the unblocking of more than one thread blocked on a condition variable. For ex‐
       ample, consider the following partial implementation of pthread_cond_wait() and pthread_cond_signal(), executed by two threads in the order given. One thread is trying to wait on
       the condition variable, another is concurrently executing pthread_cond_signal(), while a third thread is already waiting.
       在多处理器上， pthread_cond_signal() 的实现可能无法避免解除阻塞在条件变量上的多个线程。例如，考虑以下 pthread_cond_wait() 和 pthread_cond_signal() 的部分实现，由两个线程按照给定的顺序执行。一个线程试图等待条件变量，另一个线程同时执行 pthread_cond_signal()，而第三个线程已经在等待。

           pthread_cond_wait(mutex, cond):
               value = cond->value; /* 1 */
               pthread_mutex_unlock(mutex); /* 2 */
               pthread_mutex_lock(cond->mutex); /* 10 */
               if (value == cond->value) { /* 11 */
                   me->next_cond = cond->waiter;
                   cond->waiter = me;
                   pthread_mutex_unlock(cond->mutex);
                   unable_to_run(me);
               } else
                   pthread_mutex_unlock(cond->mutex); /* 12 */
               pthread_mutex_lock(mutex); /* 13 */

           pthread_cond_signal(cond):
               pthread_mutex_lock(cond->mutex); /* 3 */
               cond->value++; /* 4 */
               if (cond->waiter) { /* 5 */
                   sleeper = cond->waiter; /* 6 */
                   cond->waiter = sleeper->next_cond; /* 7 */
                   able_to_run(sleeper); /* 8 */
               }
               pthread_mutex_unlock(cond->mutex); /* 9 */

       The effect is that more than one thread can return from its call to pthread_cond_wait() or pthread_cond_timedwait() as a result of one call to pthread_cond_signal().  This effect
       is called ``spurious wakeup''.  Note that the situation is self-correcting in that the number of threads that are so awakened is finite; for example,  the  next  thread  to  call
       pthread_cond_wait() after the sequence of events above blocks.
       其结果是，作为对 pthread_cond_signal() 的一次调用的结果，多个线程可以从其对 pthread_cond_wait() 或 pthread_cond_timedwait() 的调用中返回。这种效应称为“虚假唤醒”。请注意，这种情况会自我纠正，因为被唤醒的线程数量是有限的；例如，在上面的事件序列阻塞之后调用 pthread_cond_wait() 的下一个线程。

       While  this problem could be resolved, the loss of efficiency for a fringe condition that occurs only rarely is unacceptable, especially given that one has to check the predicate
       associated with a condition variable anyway. Correcting this problem would unnecessarily reduce the degree of concurrency in this basic building block for all  higher-level  syn‐
       chronization operations.
       虽然这个问题可以解决，但很少发生的边缘条件的效率损失是不可接受的，特别是考虑到无论如何都必须检查与条件变量关联的谓词。纠正这个问题会不必要地降低这个基本构建块中所有更高级别同步操作的并发度。

       An  added benefit of allowing spurious wakeups is that applications are forced to code a predicate-testing-loop around the condition wait.  This also makes the application toler‐
       ate superfluous condition broadcasts or signals on the same condition variable that may be coded in some other part of the application. The resulting applications are  thus  more
       robust. Therefore, POSIX.1‐2008 explicitly documents that spurious wakeups may occur.
       允许虚假唤醒的另一个好处是，应用程序被迫围绕条件等待编写谓词测试循环。这也使得应用程序可以容忍在应用程序的某些其他部分编码的相同条件变量上的多余条件广播或信号。由此产生的应用程序更加健壮。因此，POSIX.1-2008 明确记录了可能发生虚假唤醒的情况。

FUTURE DIRECTIONS
       None.

SEE ALSO
       pthread_cond_destroy(), pthread_cond_timedwait()

       The Base Definitions volume of POSIX.1‐2008, Section 4.11, Memory Synchronization, <pthread.h>

COPYRIGHT
       Portions  of this text are reprinted and reproduced in electronic form from IEEE Std 1003.1, 2013 Edition, Standard for Information Technology -- Portable Operating System Inter‐
       face (POSIX), The Open Group Base Specifications Issue 7, Copyright (C) 2013 by the Institute of Electrical  and  Electronics  Engineers,  Inc  and  The  Open  Group.   (This  is
       POSIX.1-2008  with the 2013 Technical Corrigendum 1 applied.) In the event of any discrepancy between this version and the original IEEE and The Open Group Standard, the original
       IEEE and The Open Group Standard is the referee document. The original Standard can be obtained online at http://www.unix.org/online.html .

       Any typographical or formatting errors that appear in this page are most likely to have been introduced during the conversion of the source files to man page  format.  To  report
       such errors, see https://www.kernel.org/doc/man-pages/reporting_bugs.html .

IEEE/The Open Group                                                                        2013                                                            PTHREAD_COND_BROADCAST(3POSIX)


pthread_cond_wait():
pthread_cond_timedwait();

PTHREAD_COND_TIMEDWAIT(3POSIX)                                                  POSIX Programmer's Manual                                                  PTHREAD_COND_TIMEDWAIT(3POSIX)

PROLOG
       This  manual  page  is  part  of the POSIX Programmer's Manual.  The Linux implementation of this interface may differ (consult the corresponding Linux manual page for details of
       Linux behavior), or the interface may not be implemented on Linux.

NAME
       pthread_cond_timedwait, pthread_cond_wait — wait on a condition
       等待一个条件变量。

SYNOPSIS
       #include <pthread.h>


       int pthread_cond_timedwait(pthread_cond_t *restrict cond,
           pthread_mutex_t *restrict mutex,
           const struct timespec *restrict abstime);

       // wait();是死心眼的等, 死等。
       //
       // pthread_cond_wait();就相等于解锁等待。

       int pthread_cond_wait(pthread_cond_t *restrict cond,
           pthread_mutex_t *restrict mutex);

DESCRIPTION
       The pthread_cond_timedwait() and pthread_cond_wait() functions shall block on a condition variable. The application shall ensure that these functions are called with mutex locked
       by the calling thread; otherwise, an error (for PTHREAD_MUTEX_ERRORCHECK and robust mutexes) or undefined behavior (for other mutexes) results.
       pthread_cond_timedwait() 和 pthread_cond_wait() 函数应在条件变量上阻塞。应用程序应确保在调用线程锁定互斥锁的情况下调用这些函数；否则，会导致错误（对于 PTHREAD_MUTEX_ERRORCHECK 和强大的互斥锁）或未定义的行为（对于其他互斥锁）。

       These  functions  atomically  release mutex and cause the calling thread to block on the condition variable cond; atomically here means ``atomically with respect to access by an‐
       other thread to the mutex and then the condition variable''. That is, if another thread is able to acquire the mutex after the about-to-block thread has released it, then a  sub‐
       sequent call to pthread_cond_broadcast() or pthread_cond_signal() in that thread shall behave as if it were issued after the about-to-block thread has blocked.
       这些函数原子地释放互斥锁并导致调用线程在条件变量 cond 上阻塞；原子地这里的意思是“原子地相对于另一个线程访问互斥锁然后访问条件变量”。也就是说，如果另一个线程能够在即将阻塞的线程释放它之后获取互斥锁，那么在该线程中对 pthread_cond_broadcast() 或 pthread_cond_signal() 的后续调用应该表现得好像它是在即将阻塞的线程已阻塞。

       Upon successful return, the mutex shall have been locked and shall be owned by the calling thread. If mutex is a robust mutex where an owner terminated while holding the lock and
       the state is recoverable, the mutex shall be acquired even though the function returns an error code.
       成功返回后，互斥锁应已锁定并由调用线程拥有。如果互斥锁是一个健壮的互斥锁，所有者在持有锁时终止并且状态是可恢复的，则即使函数返回错误代码，也应获取互斥锁。

       When using condition variables there is always a Boolean predicate involving shared variables associated with each condition wait that is true if the thread should proceed.  Spu‐
       rious  wakeups  from the pthread_cond_timedwait() or pthread_cond_wait() functions may occur. Since the return from pthread_cond_timedwait() or pthread_cond_wait() does not imply
       anything about the value of this predicate, the predicate should be re-evaluated upon such return.
       使用条件变量时，总是有一个布尔谓词涉及与每个条件等待关联的共享变量，如果线程应该继续，则该谓词为真。可能会发生来自 pthread_cond_timedwait() 或 pthread_cond_wait() 函数的虚假唤醒。由于从 pthread_cond_timedwait() 或 pthread_cond_wait() 返回的值并不暗示有关此谓词的任何值，因此应在此类返回时重新评估谓词。

       When a thread waits on a condition variable, having specified a particular mutex to either the pthread_cond_timedwait() or the pthread_cond_wait() operation, a dynamic binding is
       formed  between  that  mutex and condition variable that remains in effect as long as at least one thread is blocked on the condition variable. During this time, the effect of an
       attempt by any thread to wait on that condition variable using a different mutex is undefined. Once all waiting threads have been unblocked (as  by  the  pthread_cond_broadcast()
       operation),  the  next wait operation on that condition variable shall form a new dynamic binding with the mutex specified by that wait operation. Even though the dynamic binding
       between condition variable and mutex may be removed or replaced between the time a thread is unblocked from a wait on the condition variable and the time that it returns  to  the
       caller or begins cancellation cleanup, the unblocked thread shall always re-acquire the mutex specified in the condition wait operation call from which it is returning.
       当一个线程等待一个条件变量时，并为 pthread_cond_timedwait() 或 pthread_cond_wait() 操作指定了一个特定的互斥锁，在该互斥锁和条件变量之间形成一个动态绑定，只要至少有一个线程在在条件变量上阻塞。在此期间，任何线程尝试使用不同的互斥锁等待该条件变量的效果是不确定的。一旦所有等待线程都被解除阻塞（如通过 pthread_cond_broadcast() 操作），对该条件变量的下一个等待操作将与该等待操作指定的互斥体形成一个新的动态绑定。即使在线程从条件变量上的等待解除阻塞到它返回到调用者或开始取消清除的时间之间可能会删除或替换条件变量和互斥锁之间的动态绑定，未阻塞的线程应始终重新获取在它返回的条件等待操作调用中指定的互斥锁。

       A condition wait (whether timed or not) is a cancellation point. When the cancelability type of a thread is set to PTHREAD_CANCEL_DEFERRED, a side-effect of acting upon a cancel‐
       lation request while in a condition wait is that the mutex is (in effect) re-acquired before calling the first cancellation cleanup handler. The effect is as if the  thread  were
       unblocked,  allowed  to  execute up to the point of returning from the call to pthread_cond_timedwait() or pthread_cond_wait(), but at that point notices the cancellation request
       and instead of returning to the caller of pthread_cond_timedwait() or pthread_cond_wait(), starts the thread cancellation activities, which includes calling cancellation  cleanup
       handlers.
       条件等待（无论是否计时）是取消点。当线程的可取消性类型设置为 PTHREAD_CANCEL_DEFERRED 时，在条件等待中对取消请求进行操作的副作用是在调用第一个取消清除处理程序之前（实际上）重新获取互斥锁。效果就像线程被解除阻塞一样，允许执行直到从调用 pthread_cond_timedwait() 或 pthread_cond_wait() 返回，但此时会注意到取消请求，而不是返回到 pthread_cond_timedwait() 的调用者或 pthread_cond_wait()，启动线程取消活动，包括调用取消清理处理程序。

       A  thread  that  has been unblocked because it has been canceled while blocked in a call to pthread_cond_timedwait() or pthread_cond_wait() shall not consume any condition signal
       that may be directed concurrently at the condition variable if there are other threads blocked on the condition variable.
       由于在调用 pthread_cond_timedwait() 或 pthread_cond_wait() 时被阻塞而被取消阻塞的线程不应消耗任何可能并发指向条件变量的条件信号，如果条件变量上有其他线程阻塞。

       The pthread_cond_timedwait() function shall be equivalent to pthread_cond_wait(), except that an error is returned if the absolute time specified by abstime passes (that is, sys‐
       tem  time equals or exceeds abstime) before the condition cond is signaled or broadcasted, or if the absolute time specified by abstime has already been passed at the time of the
       call. When such timeouts occur, pthread_cond_timedwait() shall nonetheless release and re-acquire the mutex referenced by mutex, and may consume a condition signal directed  con‐
       currently at the condition variable.
       pthread_cond_timedwait() 函数应与 pthread_cond_wait() 等效，但如果在条件 cond 发出信号或广播之前经过 abstime 指定的绝对时间（即系统时间等于或超过 abstime），则返回错误，或如果 abstime 指定的绝对时间在调用时已经过去。当这种超时发生时，pthread_cond_timedwait() 仍应释放并重新获取互斥锁引用的互斥锁，并可能消耗并发指向条件变量的条件信号。

       The  condition  variable  shall have a clock attribute which specifies the clock that shall be used to measure the time specified by the abstime argument. The pthread_cond_timed‐
       wait() function is also a cancellation point.
       条件变量应具有时钟属性，该属性指定用于测量由 abstime 参数指定的时间的时钟。 pthread_cond_timedwait() 函数也是一个取消点。

       If a signal is delivered to a thread waiting for a condition variable, upon return from the signal handler the thread resumes waiting for the condition variable as if it was  not
       interrupted, or it shall return zero due to spurious wakeup.
       如果将信号传递给等待条件变量的线程，则在从信号处理程序返回时，线程继续等待条件变量，就好像它没有被中断一样，或者由于虚假唤醒而返回零。

       The  behavior is undefined if the value specified by the cond or mutex argument to these functions does not refer to an initialized condition variable or an initialized mutex ob‐
       ject, respectively.
       如果这些函数的 cond 或 mutex 参数指定的值分别不引用初始化的条件变量或初始化的互斥对象，则行为未定义。

RETURN VALUE
       Except in the case of [ETIMEDOUT], all these error checks shall act as if they were performed immediately at the beginning of processing for the function and shall cause an error
       return, in effect, prior to modifying the state of the mutex specified by mutex or the condition variable specified by cond.
       除了 [ETIMEDOUT] 的情况外，所有这些错误检查都应该像在函数处理开始时立即执行一样，并且实际上在修改 mutex 指定的互斥锁状态之前会导致错误返回或由 cond 指定的条件变量。

       Upon successful completion, a value of zero shall be returned; otherwise, an error number shall be returned to indicate the error.
       成功完成后，应返回零值；否则，将返回错误编号以指示错误。

ERRORS
       These functions shall fail if:
       如果出现以下情况，这些功能将失败:

       ENOTRECOVERABLE
       not recoverable(不可恢复)
              The state protected by the mutex is not recoverable.
              互斥锁保护的状态是不可恢复的。

       EOWNERDEAD
       owner dead(拥有者死亡)
              The  mutex is a robust mutex and the process containing the previous owning thread terminated while holding the mutex lock. The mutex lock shall be acquired by the calling
              thread and it is up to the new owner to make the state consistent.
              互斥锁是一个健壮的互斥锁，包含前一个拥有线程的进程在持有互斥锁时终止。互斥锁应由调用线程获取，由新所有者来使状态保持一致。

       EPERM  The mutex type is PTHREAD_MUTEX_ERRORCHECK or the mutex is a robust mutex, and the current thread does not own the mutex.
       互斥类型为 PTHREAD_MUTEX_ERRORCHECK 或互斥为健壮互斥，且当前线程不拥有互斥。

       The pthread_cond_timedwait() function shall fail if:
       如果出现以下情况，pthread_cond_timedwait() 函数将失败:

       ETIMEDOUT
       timed out(时间到)
              The time specified by abstime to pthread_cond_timedwait() has passed.
              abstime 指定到 pthread_cond_timedwait() 的时间已经过去。

       EINVAL
       invalid(无效的)
            The abstime argument specified a nanosecond value less than zero or greater than or equal to 1000 million.
            abstime 参数指定了一个小于零或大于或等于 10 亿的纳秒值。


       These functions may fail if:
       如果出现以下情况，这些功能可能会失败:

       EOWNERDEAD
              The mutex is a robust mutex and the previous owning thread terminated while holding the mutex lock. The mutex lock shall be acquired by the calling thread and it is up  to
              the new owner to make the state consistent.
              互斥锁是一个健壮的互斥锁，之前拥有的线程在持有互斥锁时终止。互斥锁应由调用线程获取，由新所有者来使状态保持一致。

       These functions shall not return an error code of [EINTR].
       这些函数不应返回 [EINTR] 的错误代码。

       The following sections are informative.
       以下部分是信息性的。

EXAMPLES
       None.

APPLICATION USAGE
       Applications that have assumed that non-zero return values are errors will need updating for use with robust mutexes, since a valid return for a thread acquiring a mutex which is
       protecting a currently inconsistent state is [EOWNERDEAD].  Applications that do not check the error returns, due to ruling out the possibility of such errors arising, should not
       use robust mutexes. If an application is supposed to work with normal and robust mutexes, it should check all return values for error conditions and if necessary take appropriate
       action.
       假设非零返回值是错误的应用程序将需要更新以与强大的互斥锁一起使用，因为获取保护当前不一致状态的互斥锁的线程的有效返回是 [EOWNERDEAD]。不检查错误返回的应用程序，由于排除了出现此类错误的可能性，不应使用健壮的互斥锁。如果应用程序应该使用正常和健壮的互斥锁，它应该检查所有返回值的错误情况，并在必要时采取适当的措施。

RATIONALE
       If an implementation detects that the value specified by the cond argument to pthread_cond_timedwait() or pthread_cond_wait() does not refer to an initialized condition variable,
       or detects that the value specified by the mutex argument to pthread_cond_timedwait() or pthread_cond_wait() does not refer to an initialized mutex object, it is recommended that
       the function should fail and report an [EINVAL] error.
       如果实现检测到 pthread_cond_timedwait() 或 pthread_cond_wait() 的 cond 参数指定的值不引用初始化的条件变量，或者检测到 pthread_cond_timedwait() 或 pthread_cond_wait() 的互斥参数指定的值不引用对于已初始化的互斥对象，建议该函数应失败并报告 [EINVAL] 错误。

   Condition Wait Semantics
   条件等待语义
       It is important to note that when pthread_cond_wait() and pthread_cond_timedwait()  return  without  error,  the  associated  predicate  may  still  be  false.   Similarly,  when
       pthread_cond_timedwait()  returns  with the timeout error, the associated predicate may be true due to an unavoidable race between the expiration of the timeout and the predicate
       state change.
       需要注意的是，当 pthread_cond_wait() 和 pthread_cond_timedwait() 没有错误返回时，关联的谓词可能仍然为假。类似地，当 pthread_cond_timedwait() 返回超时错误时，由于超时到期和谓词状态更改之间不可避免的竞争，关联的谓词可能为真。

       The application needs to recheck the predicate on any return because it cannot be sure there is another thread waiting on the thread to handle the signal, and  if  there  is  not
       then the signal is lost. The burden is on the application to check the predicate.
       应用程序需要在任何返回时重新检查谓词，因为它无法确定是否有另一个线程在等待该线程处理信号，如果没有，则信号丢失。检查谓词的责任在于应用程序。

       Some  implementations, particularly on a multi-processor, may sometimes cause multiple threads to wake up when the condition variable is signaled simultaneously on different pro‐
       cessors.
       某些实现，尤其是在多处理器上，有时可能会导致多个线程在不同处理器上同时发出条件变量信号时唤醒。

       In general, whenever a condition wait returns, the thread has to re-evaluate the predicate associated with the condition wait to determine whether it can safely  proceed,  should
       wait again, or should declare a timeout. A return from the wait does not imply that the associated predicate is either true or false.
       通常，每当条件等待返回时，线程必须重新评估与条件等待关联的谓词，以确定它是否可以安全地继续、应该再次等待还是应该声明超时。等待的返回并不意味着关联的谓词为真或假。

       It is thus recommended that a condition wait be enclosed in the equivalent of a ``while loop'' that checks the predicate.
       因此，建议将条件等待包含在相当于检查谓词的“while 循环”中。

   Timed Wait Semantics
   定时等待语义
       An absolute time measure was chosen for specifying the timeout parameter for two reasons. First, a relative time measure can be easily implemented on top of a function that spec‐
       ifies absolute time, but there is a race condition associated with specifying an absolute timeout on top of a function that specifies relative timeouts. For example, assume  that
       clock_gettime() returns the current time and cond_relative_timed_wait() uses relative timeouts:
       出于两个原因，选择了绝对时间度量来指定超时参数。首先，可以在指定绝对时间的函数之上轻松实现相对时间度量，但是在指定相对超时的函数之上指定绝对超时存在竞争条件。
       例如，假设 clock_gettime() 返回当前时间并且 cond_relative_timed_wait() 使用相对超时：

           clock_gettime(CLOCK_REALTIME, &now)
           reltime = sleep_til_this_absolute_time -now;
           cond_relative_timed_wait(c, m, &reltime);

       If the thread is preempted between the first statement and the last statement, the thread blocks for too long. Blocking, however, is irrelevant if an absolute timeout is used. An
       absolute timeout also need not be recomputed if it is used multiple times in a loop, such as that enclosing a condition wait.
       如果线程在第一条语句和最后一条语句之间被抢占，则线程阻塞的时间过长。但是，如果使用绝对超时，则阻塞无关紧要。如果在循环中多次使用绝对超时，例如包含条件等待的绝对超时，也不需要重新计算。

       For cases when the system clock is advanced discontinuously by an operator, it is expected that implementations process any timed wait expiring at an intervening time as if  that
       time had actually occurred.
       对于操作员不连续地提前系统时钟的情况，期望实现处理在中间时间到期的任何定时等待，就好像该时间已经实际发生一样。

   Cancellation and Condition Wait
   取消和条件等待
       A condition wait, whether timed or not, is a cancellation point. That is, the functions pthread_cond_wait() or pthread_cond_timedwait() are points where a pending (or concurrent)
       cancellation request is noticed. The reason for this is that an indefinite wait is possible at these points—whatever event is being waited for, even if  the  program  is  totally
       correct,  might never occur; for example, some input data being awaited might never be sent. By making condition wait a cancellation point, the thread can be canceled and perform
       its cancellation cleanup handler even though it may be stuck in some indefinite wait.
       条件等待，无论是否定时，都是取消点。也就是说，函数 pthread_cond_wait() 或 pthread_cond_timedwait() 是注意到挂起（或并发）取消请求的点。这样做的原因是在这些点上可能会出现无限期等待——无论等待什么事件，即使程序完全正确，也可能永远不会发生；例如，一些正在等待的输入数据可能永远不会被发送。通过使条件等待成为取消点，线程可以被取消并执行其取消清理处理程序，即使它可能陷入某种无限期的等待。

       A side-effect of acting on a cancellation request while a thread is blocked on a condition variable is to re-acquire the mutex before calling any of the cancellation cleanup han‐
       dlers.  This  is  done in order to ensure that the cancellation cleanup handler is executed in the same state as the critical code that lies both before and after the call to the
       condition wait function. This rule is also required when interfacing to POSIX threads from languages, such as Ada or C++, which may choose to map cancellation onto a language ex‐
       ception;  this  rule  ensures that each exception handler guarding a critical section can always safely depend upon the fact that the associated mutex has already been locked re‐
       gardless of exactly where within the critical section the exception was raised. Without this rule, there would not be a uniform rule that exception handlers could follow  regard‐
       ing the lock, and so coding would become very cumbersome.
       当线程被条件变量阻塞时，处理取消请求的副作用是在调用任何取消清除处理程序之前重新获取互斥锁。这样做是为了确保取消清理处理程序在与调用条件等待函数之前和之后的关键代码相同的状态下执行。当从语言（例如 Ada 或 C++）连接到 POSIX 线程时，也需要此规则，这些语言可能会选择将取消映射到语言异常上；该规则确保保护临界区的每个异常处理程序始终可以安全地依赖于相关联的互斥锁已经被锁定的事实，而不管异常在临界区中的确切位置引发。没有这条规则，就没有异常处理程序可以遵循关于锁的统一规则，因此编码会变得非常繁琐。

       Therefore, since some statement has to be made regarding the state of the lock when a cancellation is delivered during a wait, a definition has been chosen that makes application
       coding most convenient and error free.
       因此，由于在等待期间传递取消时必须对锁的状态进行一些声明，因此选择了一种定义，使应用程序编码最方便且无错误。

       When acting on a cancellation request while a thread is blocked on a condition variable, the implementation is required to ensure that the thread does not consume  any  condition
       signals  directed  at that condition variable if there are any other threads waiting on that condition variable. This rule is specified in order to avoid deadlock conditions that
       could occur if these two independent requests (one acting on a thread and the other acting on the condition variable) were not processed independently.
       当线程在条件变量上被阻塞时处理取消请求时，如果有任何其他线程在等待该条件变量，则需要实现以确保线程不消耗任何指向该条件变量的条件信号。指定此规则是为了避免在这两个独立请求（一个作用于线程，另一个作用于条件变量）未独立处理时可能发生的死锁情况。

   Performance of Mutexes and Condition Variables
   互斥体和条件变量的性能
       Mutexes are expected to be locked only for a few instructions. This practice is almost automatically enforced by the desire of programmers to avoid long serial regions of  execu‐
       tion (which would reduce total effective parallelism).
       互斥锁预计仅针对少数指令被锁定。由于程序员希望避免长的串行执行区域（这会降低总有效并行度），这种做法几乎是自动执行的。

       When  using  mutexes and condition variables, one tries to ensure that the usual case is to lock the mutex, access shared data, and unlock the mutex. Waiting on a condition vari‐
       able should be a relatively rare situation. For example, when implementing a read-write lock, code that acquires a read-lock typically needs only to increment the count of  read‐
       ers (under mutual-exclusion) and return. The calling thread would actually wait on the condition variable only when there is already an active writer. So the efficiency of a syn‐
       chronization operation is bounded by the cost of mutex lock/unlock and not by condition wait. Note that in the usual case there is no context switch.
       使用互斥锁和条件变量时，会尽量确保通常的情况是锁定互斥锁、访问共享数据和解锁互斥锁。等待条件变量应该是一种相对罕见的情况。例如，在实现读写锁时，获取读锁的代码通常只需要增加读取者的计数（在互斥情况下）并返回。只有当已经有一个活动的编写器时，调用线程才会实际等待条件变量。因此，同步操作的效率受互斥锁解锁成本的限制，而不受条件等待的限制。请注意，在通常情况下，没有上下文切换。

       This is not to say that the efficiency of condition waiting is unimportant. Since there needs to be at least one context switch per Ada rendezvous, the efficiency of waiting on a
       condition variable is important. The cost of waiting on a condition variable should be little more than the minimal cost for a context switch plus the time to unlock and lock the
       mutex.
       这并不是说条件等待的效率不重要。由于每个 Ada 集合点至少需要一个上下文切换，因此等待条件变量的效率很重要。等待条件变量的成本应该比上下文切换的最小成本加上解锁和锁定互斥锁的时间多一点。

   Features of Mutexes and Condition Variables
   互斥体和条件变量的特点
       It had been suggested that the mutex acquisition and release be decoupled from condition wait. This was rejected because it is the combined nature of the operation that, in fact,
       facilitates  realtime implementations. Those implementations can atomically move a high-priority thread between the condition variable and the mutex in a manner that is transpar‐
       ent to the caller. This can prevent extra context switches and provide more deterministic acquisition of a mutex when the waiting thread is signaled. Thus, fairness and  priority
       issues can be dealt with directly by the scheduling discipline.  Furthermore, the current condition wait operation matches existing practice.
       有人建议将互斥锁的获取和释放与条件等待分离。这被拒绝了，因为实际上是操作的组合性质促进了实时实现。这些实现可以以对调用者透明的方式在条件变量和互斥锁之间原子地移动高优先级线程。这可以防止额外的上下文切换，并在等待线程收到信号时提供更多确定性的互斥锁获取。因此，公平性和优先级问题可以由调度规程直接处理。此外，当前条件等待操作与现有实践相匹配。

   Scheduling Behavior of Mutexes and Condition Variables
   互斥体和条件变量的调度行为
       Synchronization  primitives  that attempt to interfere with scheduling policy by specifying an ordering rule are considered undesirable.  Threads waiting on mutexes and condition
       variables are selected to proceed in an order dependent upon the scheduling policy rather than in some fixed order (for example, FIFO or priority). Thus,  the  scheduling  policy
       determines which thread(s) are awakened and allowed to proceed.
       试图通过指定排序规则来干扰调度策略的同步原语被认为是不受欢迎的。等待互斥体和条件变量的线程被选择以依赖于调度策略的顺序而不是某些固定顺序（例如，FIFO 或优先级）进行。因此，调度策略确定哪些线程被唤醒并允许继续进行。

   Timed Condition Wait
   定时条件等待
       The pthread_cond_timedwait() function allows an application to give up waiting for a particular condition after a given amount of time. An example of its use follows:
       pthread_cond_timedwait() 函数允许应用程序在给定的时间后放弃等待特定条件。其使用示例如下:

           (void) pthread_mutex_lock(&t.mn);
               t.waiters++;
               clock_gettime(CLOCK_REALTIME, &ts);
               ts.tv_sec += 5;
               rc = 0;
               while (! mypredicate(&t) && rc == 0)
                   rc = pthread_cond_timedwait(&t.cond, &t.mn, &ts);
               t.waiters--;
               if (rc == 0 || mypredicate(&t))
                   setmystate(&t);
           (void) pthread_mutex_unlock(&t.mn);

       By  making the timeout parameter absolute, it does not need to be recomputed each time the program checks its blocking predicate. If the timeout was relative, it would have to be
       recomputed before each call.  This would be especially difficult since such code would need to take into account the possibility of extra wakeups that result  from  extra  broad‐
       casts or signals on the condition variable that occur before either the predicate is true or the timeout is due.
       通过将超时参数设为绝对参数，程序每次检查其阻塞谓词时都不需要重新计算。如果超时是相对的，则必须在每次调用之前重新计算。这将特别困难，因为此类代码需要考虑在谓词为真或超时到期之前发生的额外广播或条件变量上的信号导致的额外唤醒的可能性。

FUTURE DIRECTIONS
       None.

SEE ALSO
       pthread_cond_broadcast()

       The Base Definitions volume of POSIX.1‐2008, Section 4.11, Memory Synchronization, <pthread.h>

COPYRIGHT
       Portions  of this text are reprinted and reproduced in electronic form from IEEE Std 1003.1, 2013 Edition, Standard for Information Technology -- Portable Operating System Inter‐
       face (POSIX), The Open Group Base Specifications Issue 7, Copyright (C) 2013 by the Institute of Electrical  and  Electronics  Engineers,  Inc  and  The  Open  Group.   (This  is
       POSIX.1-2008  with the 2013 Technical Corrigendum 1 applied.) In the event of any discrepancy between this version and the original IEEE and The Open Group Standard, the original
       IEEE and The Open Group Standard is the referee document. The original Standard can be obtained online at http://www.unix.org/online.html .

       Any typographical or formatting errors that appear in this page are most likely to have been introduced during the conversion of the source files to man page  format.  To  report
       such errors, see https://www.kernel.org/doc/man-pages/reporting_bugs.html .

IEEE/The Open Group                                                                        2013                                                            PTHREAD_COND_TIMEDWAIT(3POSIX)

]]


#[[
使用条件变量解决 5 版本之前的盲等。

加入条件变量后不是盲等版本了。CPU利用率没有出现飙升的情况。

成功了。
]]
add_subdirectory(mytbf_multithread6)


#[[
加入条件变量, 重构之前盲等版的判断质数程序。

成功运行没有问题, 重构成功。
]]
add_executable(thread-pool-primer3 thread-pool-primer3.cpp)


#[[
使用锁链, 即线程1执行了后去解锁线程2, 线程2执行了后去解锁线程3, 线程3执行了后去解锁线程4,
线程4执行了后去解锁线程1。
这里其实是妙用死锁。

加入条件变量, 重构之前的 abcd 打印程序。
打印完a的线程去通知b线程去打印b, 打印完b的线程去通知打印c的线程打印c。

重构完成, 且运行没有问题。
]]
add_executable(thread-abcd3 thread-abcd3.cpp)


#[[
信号量(资源总量)

互斥量太独占了, 有时不是一个好的解决办法, 例如: 哲学家就餐问题。

之后复习到进程间通信的时候, 会有信号量数组(其实就是多个信号量来同时使用)。

可以理解:
互斥量就是 bool 类型, 不是是就是否。
而信号量可以理解为 int 类型, 有加有减两个操作。 (生产者消费者)
如果用的话, 必须要够减, 你才能够使用。如果使用完了, 你就要归还, 累加这个资源量。
如果使用一个资源的时候, 发现不够减了, 那就等待。

用 互斥量 + 条件变量 来完成一个信号量的使用。
(用互斥量和条件变量来完成一个能够记次数的有资源上限的资源的共享。)

semaphore: 信号量。

这次的实现是, 将C在头文件中提供的接口封装到了一个工具类中, 全都设计成了 static 方法/函数。

可以成功运行, 没有问题。

]]
add_subdirectory(mysem)

#[[
线程同步
    互斥量
    条件变量
    信号量(是互斥量和条件变量综合在一起使用)
    读写锁(是互斥量和信号量综合在一起使用)
        读写锁包含两种:
        读锁: 相当于共享的资源, 相当于信号量(要有一个资源的上限)。
        写锁: 相当于独占的资源, 相当于互斥量。
        写者饿死问题。

线程同步中使用最多的就是互斥量和条件变量！
这两个同步进制解决不了问题的时候, 再去想信号量和读写锁这些同步机制。

]]


#[[

线程属性:
pthread_attr_init();
pthread_attr_destroy();
...

PTHREAD_ATTR_INIT(3)                                              Linux Programmer's Manual                                              PTHREAD_ATTR_INIT(3)

NAME
       pthread_attr_init, pthread_attr_destroy - initialize and destroy thread attributes object
       初始化和销毁线程属性对象。

SYNOPSIS
       #include <pthread.h>

       int pthread_attr_init(pthread_attr_t *attr);
       int pthread_attr_destroy(pthread_attr_t *attr);

       Compile and link with -pthread.

DESCRIPTION
       The pthread_attr_init() function initializes the thread attributes object pointed to by attr with default attribute values.  After this call, individ‐
       ual attributes of the object can be set using various related functions (listed under SEE ALSO), and then the object  can  be  used  in  one  or  more
       pthread_create(3) calls that create threads.
       pthread_attr_init() 函数使用默认属性值初始化由 attr 指向的线程属性对象。在此调用之后，可以使用各种相关函数（在 SEE ALSO 下列出）设置对象的各个属性，然后可以在一个或多个创建线程的 pthread_create(3) 调用中使用该对象。

       Calling pthread_attr_init() on a thread attributes object that has already been initialized results in undefined behavior.
       在已初始化的线程属性对象上调用 pthread_attr_init() 会导致未定义的行为。

       When  a  thread  attributes  object  is no longer required, it should be destroyed using the pthread_attr_destroy() function.  Destroying a thread at‐
       tributes object has no effect on threads that were created using that object.
       当不再需要线程属性对象时，应使用 pthread_attr_destroy() 函数销毁它。销毁线程属性对象对使用该对象创建的线程没有影响。

       Once a thread attributes object has been destroyed, it can be reinitialized using pthread_attr_init().  Any other use of a destroyed thread attributes
       object has undefined results.
       一旦线程属性对象被销毁，就可以使用 pthread_attr_init() 重新初始化它。被破坏的线程属性对象的任何其他使用都有未定义的结果。

RETURN VALUE
       On success, these functions return 0; on error, they return a nonzero error number.
       成功时，这些函数返回 0；出错时，它们返回一个非零错误号。


-------------------------------------------------

互斥量属性:
pthread_mutexattr_init();
pthread_mutexattr_destroy();
...

--------------------------------------------------

补充:
clone();是系统调用, 创建一个子进程。
这个函数可以创建一个既不是子进程也不是线程的一个内容, 来满足当前开发的需要。

--------------------------------------------------

条件变量属性:
pthread_condattr_init();
pthread_condattr_destroy();
...

读写锁属性:
...

]]


#[[
重入: reenterable 可重入的。

posix规定: 如果发布出来的一个库, 那么这个库应该本身就支持多线程并发。
如果不能支持多线程并发, 那么一定要在说明文档中体现出来。

多线程中的IO: 所有的标准IO函数, 都支持多线程并发。
线程1 -> puts("aaaaa");
线程2 -> puts("bbbbb");
线程3 -> puts("ccccc");
为什么看不到 abcabcabc... 这样的输出?
是因为 puts(); 有这样的功能:
先锁住缓冲区, 把内容填进去, 然后对缓冲区解锁,
这三个线程在运行的时候, 几乎同时调用 puts();的时候,
那就看谁先抢到缓冲区。谁先抢到缓冲区, 谁就先将内容放到缓冲区中。

再如: getchar(); 函数也是这样的, 内部使用到了互斥量和条件变量的时候。
之前写的都是单线程的main程序, 而调用 getchar();需要不停的加锁解锁,
是没有必要的, 如果在单线程中。

所以 getchar(); 提供了 getchar_unlocked();版本,
这个版本是一个没有加锁的版本。

todo 笔记: 所以一个函数如果并不支持多线程并发, 那么一定要从函数名字上体现出来。



UNLOCKED_STDIO(3)                                                               Linux Programmer's Manual                                                               UNLOCKED_STDIO(3)

NAME
       getc_unlocked, getchar_unlocked, putc_unlocked, putchar_unlocked - nonlocking stdio functions

SYNOPSIS
       #include <stdio.h>

       int getc_unlocked(FILE *stream);
       int getchar_unlocked(void);
       int putc_unlocked(int c, FILE *stream);
       int putchar_unlocked(int c);

       void clearerr_unlocked(FILE *stream);
       int feof_unlocked(FILE *stream);
       int ferror_unlocked(FILE *stream);
       int fileno_unlocked(FILE *stream);
       int fflush_unlocked(FILE *stream);
       int fgetc_unlocked(FILE *stream);
       int fputc_unlocked(int c, FILE *stream);
       size_t fread_unlocked(void *ptr, size_t size, size_t n,
                             FILE *stream);
       size_t fwrite_unlocked(const void *ptr, size_t size, size_t n,
                             FILE *stream);

       char *fgets_unlocked(char *s, int n, FILE *stream);
       int fputs_unlocked(const char *s, FILE *stream);

       #include <wchar.h>

       wint_t getwc_unlocked(FILE *stream);
       wint_t getwchar_unlocked(void);
       wint_t fgetwc_unlocked(FILE *stream);
       wint_t fputwc_unlocked(wchar_t wc, FILE *stream);
       wint_t putwc_unlocked(wchar_t wc, FILE *stream);
       wint_t putwchar_unlocked(wchar_t wc);
       wchar_t *fgetws_unlocked(wchar_t *ws, int n, FILE *stream);
       int fputws_unlocked(const wchar_t *ws, FILE *stream);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       getc_unlocked(), getchar_unlocked(), putc_unlocked(), putchar_unlocked():
           /* Since glibc 2.24: */ _POSIX_C_SOURCE >= 199309L
               || /* Glibc versions <= 2.23: */ _POSIX_C_SOURCE
               || /* Glibc versions <= 2.19: */ _SVID_SOURCE || _BSD_SOURCE

       clearerr_unlocked(), feof_unlocked(), ferror_unlocked(), fileno_unlocked(), fflush_unlocked(), fgetc_unlocked(), fputc_unlocked(), fread_unlocked(), fwrite_unlocked():
           /* Glibc since 2.19: */ _DEFAULT_SOURCE
               || /* Glibc versions <= 2.19: */ _SVID_SOURCE || _BSD_SOURCE

       fgets_unlocked(), fputs_unlocked(), getwc_unlocked(), getwchar_unlocked(), fgetwc_unlocked(), fputwc_unlocked(), putwchar_unlocked(), fgetws_unlocked(), fputws_unlocked():
           _GNU_SOURCE

DESCRIPTION
       Each of these functions has the same behavior as its counterpart without the "_unlocked" suffix, except that they do not use locking (they do not set locks themselves, and do not
       test for the presence of locks set by others) and hence are thread-unsafe.  See flockfile(3).

]]


#[[
线程与信号
    一般不会将信号机制与多线程大范围的混用。在小范围的混用能解决问题是最好的方式。

其实一个进程中的每个线程都有自己的 mask位图和 pending位图,
理论上都是 32 位的。标准信号有31个。
mask位图全为1, pending位图全为0这种初始状态。

进程没有mask位图, 只有pending位图。

那么进程是如何响应信号的?
是从内核态扎会到用户态, 进程中被调度到的那个线程用自身的mask位图与
这个进程级别的pending位图按位与一次, 然后在与自身的pending按位与一次来
查看自己收到了哪些信号。

补充函数:
pthread_sigmask();
相当于进程阶段的 sigprocmask();函数。
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);



PTHREAD_SIGMASK(3)                                                              Linux Programmer's Manual                                                              PTHREAD_SIGMASK(3)

NAME
       pthread_sigmask - examine and change mask of blocked signals
       检查和更改阻塞信号的掩码。

SYNOPSIS
       #include <signal.h>

       int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);

       Compile and link with -pthread.

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       pthread_sigmask():
           _POSIX_C_SOURCE >= 199506L || _XOPEN_SOURCE >= 500

DESCRIPTION
       The  pthread_sigmask() function is just like sigprocmask(2), with the difference that its use in multithreaded programs is explicitly specified by POSIX.1.  Other differences are
       noted in this page.
       pthread_sigmask() 函数就像 sigprocmask(2) 一样，不同之处在于它在多线程程序中的使用由 POSIX.1 明确指定。本页指出了其他差异。

       For a description of the arguments and operation of this function, see sigprocmask(2).
       有关此函数的参数和操作的说明，请参阅 sigprocmask(2)。

RETURN VALUE
       On success, pthread_sigmask() returns 0; on error, it returns an error number.
       成功时，pthread_sigmask() 返回 0；出错时，它返回一个错误号。

ERRORS
       See sigprocmask(2).

ATTRIBUTES
       For an explanation of the terms used in this section, see attributes(7).

       ┌──────────────────┬───────────────┬─────────┐
       │Interface         │ Attribute     │ Value   │
       ├──────────────────┼───────────────┼─────────┤
       │pthread_sigmask() │ Thread safety │ MT-Safe │
       └──────────────────┴───────────────┴─────────┘
CONFORMING TO
       POSIX.1-2001, POSIX.1-2008.

NOTES
       A new thread inherits a copy of its creator's signal mask.
       新线程继承其创建者信号掩码的副本。

       The glibc pthread_sigmask() function silently ignores attempts to block the two real-time signals that are used internally by the NPTL threading implementation.  See nptl(7)  for
       details.
       glibc pthread_sigmask() 函数会默默地忽略阻止 NPTL 线程实现内部使用的两个实时信号的尝试。有关详细信息，请参阅 nptl(7)。

SEE ALSO
       sigaction(2), sigpending(2), sigprocmask(2), pthread_create(3), pthread_kill(3), sigsetops(3), pthreads(7), signal(7)

COLOPHON
       This page is part of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest version of  this  page,  can  be
       found at https://www.kernel.org/doc/man-pages/.

Linux                                                                                   2019-03-06                                                                     PTHREAD_SIGMASK(3)


sigwait();
相当于 pause();函数 但是是线程级别的。

SIGWAIT(3)                                                                      Linux Programmer's Manual                                                                      SIGWAIT(3)

NAME
       sigwait - wait for a signal

SYNOPSIS
       #include <signal.h>

        int sigwait(const sigset_t *set, int *sig);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       sigwait():
           Since glibc 2.26:
               _POSIX_C_SOURCE >= 199506L
           Glibc 2.25 and earlier:
               _POSIX_C_SOURCE

DESCRIPTION
       The  sigwait()  function  suspends execution of the calling thread until one of the signals specified in the signal set set becomes pending.  The function accepts the signal (re‐
       moves it from the pending list of signals), and returns the signal number in sig.
       sigwait() 函数会挂起调用线程的执行，直到信号集集中指定的信号之一变为挂起状态。该函数接受信号（将其从待处理的信号列表中移除），并在 sig 中返回信号编号。

       The operation of sigwait() is the same as sigwaitinfo(2), except that:
       sigwait() 的操作与 sigwaitinfo(2) 相同，不同之处在于:

       * sigwait() returns only the signal number, rather than a siginfo_t structure describing the signal.
       * sigwait() 仅返回信号编号，而不是描述信号的 siginfo_t 结构。

       * The return values of the two functions are different.
       * 两个函数的返回值不同。

RETURN VALUE
       On success, sigwait() returns 0.  On error, it returns a positive error number (listed in ERRORS).
       成功时，sigwait() 返回 0。错误时，它返回一个正错误号（在 ERRORS 中列出）。

ERRORS
       EINVAL set contains an invalid signal number.
       set 包含无效的信号编号。

ATTRIBUTES
       For an explanation of the terms used in this section, see attributes(7).

       ┌──────────┬───────────────┬─────────┐
       │Interface │ Attribute     │ Value   │
       ├──────────┼───────────────┼─────────┤
       │sigwait() │ Thread safety │ MT-Safe │
       └──────────┴───────────────┴─────────┘
CONFORMING TO
       POSIX.1-2001, POSIX.1-2008.

NOTES
       sigwait() is implemented using sigtimedwait(2).
       sigwait() 是使用 sigtimedwait(2) 实现的。

       The glibc implementation of sigwait() silently ignores attempts to wait for the two real-time signals that are used internally by the NPTL threading implementation.  See  nptl(7)
       for details.
       sigwait() 的 glibc 实现会默默地忽略等待 NPTL 线程实现内部使用的两个实时信号的尝试。有关详细信息，请参阅 nptl(7)。

EXAMPLE
       See pthread_sigmask(3).

SEE ALSO
       sigaction(2), signalfd(2), sigpending(2), sigsuspend(2), sigwaitinfo(2), sigsetops(3), signal(7)

COLOPHON
       This  page  is  part  of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest version of this page, can be
       found at https://www.kernel.org/doc/man-pages/.

Linux                                                                                   2017-07-13                                                                             SIGWAIT(3)


pthread_kill();函数是线程阶段用来发信号的,
相当于 kill();函数。

PTHREAD_KILL(3)                                                                 Linux Programmer's Manual                                                                 PTHREAD_KILL(3)

NAME
       pthread_kill - send a signal to a thread

SYNOPSIS
       #include <signal.h>

       int pthread_kill(pthread_t thread, int sig);

       Compile and link with -pthread.

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       pthread_kill():
           _POSIX_C_SOURCE >= 199506L || _XOPEN_SOURCE >= 500

DESCRIPTION
       The pthread_kill() function sends the signal sig to thread, a thread in the same process as the caller.  The signal is asynchronously directed to thread.
       pthread_kill() 函数将信号 sig 发送到线程，该线程与调用者处于同一进程中。信号异步定向到线程。

       If sig is 0, then no signal is sent, but error checking is still performed.
       如果 sig 为 0，则不发送信号，但仍会执行错误检查。

RETURN VALUE
       On success, pthread_kill() returns 0; on error, it returns an error number, and no signal is sent.
       成功时，pthread_kill() 返回 0；出错时，它返回一个错误号，并且不发送任何信号。

ERRORS
       EINVAL An invalid signal was specified.
       指定了无效信号。

ATTRIBUTES
       For an explanation of the terms used in this section, see attributes(7).

       ┌───────────────┬───────────────┬─────────┐
       │Interface      │ Attribute     │ Value   │
       ├───────────────┼───────────────┼─────────┤
       │pthread_kill() │ Thread safety │ MT-Safe │
       └───────────────┴───────────────┴─────────┘
CONFORMING TO
       POSIX.1-2001, POSIX.1-2008.

NOTES
       Signal  dispositions  are  process-wide: if a signal handler is installed, the handler will be invoked in the thread thread, but if the disposition of the signal is "stop", "con‐
       tinue", or "terminate", this action will affect the whole process.
       信号处置是进程范围的: 如果安装了信号处理程序，则该处理程序将在线程线程中被调用，但如果信号的处置是“停止”、“继续”或“终止”，则此操作将影响整个过程。

       The glibc implementation of pthread_kill() gives an error (EINVAL) on attempts to send either of the real-time signals used internally by the NPTL threading implementation.   See
       nptl(7) for details.
       pthread_kill() 的 glibc 实现在尝试发送 NPTL 线程实现内部使用的任一实时信号时给出错误 (EINVAL)。有关详细信息，请参阅 nptl(7)。

       POSIX.1-2008 recommends that if an implementation detects the use of a thread ID after the end of its lifetime, pthread_kill() should return the error ESRCH.  The glibc implemen‐
       tation returns this error in the cases where an invalid thread ID can be detected.  But note also that POSIX says that an attempt to use a thread ID whose lifetime has ended pro‐
       duces undefined behavior, and an attempt to use an invalid thread ID in a call to pthread_kill() can, for example, cause a segmentation fault.
       POSIX.1-2008 建议，如果实现在其生命周期结束后检测到线程 ID 的使用，pthread_kill() 应返回错误 ESRCH。在可以检测到无效线程 ID 的情况下，glibc 实现会返回此错误。但也要注意 POSIX 说尝试使用生命周期已经结束的线程 ID 会产生未定义的行为，并且在调用 pthread_kill() 时尝试使用无效的线程 ID 会导致段错误。

SEE ALSO
       kill(2), sigaction(2), sigpending(2), pthread_self(3), pthread_sigmask(3), raise(3), pthreads(7), signal(7)

COLOPHON
       This  page  is  part  of release 5.05 of the Linux man-pages project.  A description of the project, information about reporting bugs, and the latest version of this page, can be
       found at https://www.kernel.org/doc/man-pages/.

Linux                                                                                   2017-09-15                                                                        PTHREAD_KILL(3)


线程与信号
    pthread_sigmask();
    sigwait();
    pthread_kill();

]]


#[[
线程与 fork

如果当前的进程空间里面有多个线程, 那么在某个线程指定 fork();后会发生什么?
posix中规定新的进程当中只包含调用fork();的那一个线程。

还有其他标准是姓的进程当中是和原来的进程一模一样的。

]]


#[[
线程标准除了posix标准还有其它的标准的, 例如 openmp线程标准。

最根本的原因是C语言和C++语言, 因为这两种语言本身对并发支持不友好。

]]


#[[
多线程并发的工作模式
1. 流水线模式: 每个线程负责一个环节, 一个线程做完后, 第二个线程执行, 然后第三个线程执行... 例如: abcd
2. 工作组模式: 一个大任务, 分散成若干个小任务, 每个线程做一个这样的小任务。 例如: primer
3. 一个线程在做某项任务, 然后供其它线程使用。 例如: 多线程版的令牌桶/定时器。
4. C/S模式: 一个服务器, 多个客户端。

]]


#[[
将之前的 anytimer 定时器重构为多线程安全版本。

重构完成, 可以运行成功, 没有问题。

但是这个任务函数的执行还是在 alarm线程中, 可以执行这个任务会耗一些时间, 造成计时不准。
所以下一个版本会将这个任务函数回调放在一个新的线程中, 一个线程负责一个定时任务。

]]
add_subdirectory(AnyTimer2_multithread)

#[[
上一个版本是不完善的, 没有考虑将一个任务放到一个线程中。
但是一个任务放到一个线程中, 又觉得太浪费了。万一全部是1000秒后再运行, 然后又200个任务的话。确实很浪费。
不过也没有关系, 就这样先实现出来再说其它的。
]]
#[[
这个 2 版本实现出来了。使用一个线程对应定时器到时后, 执行任务函数, 然后这个线程不需要为它收尸, 因为把这些任务线程分离(detach)了,
这些任务线程运行结束后, 就自己被系统回收了。

运行出来了, 运行没有错误, 可以正常退出没有内存泄漏。
但是有一点不好的是, 这个版本是盲等的版本。没有使用条件变量。
下个版本将加入条件变量。
]]
add_subdirectory(AnyTimer2_multithread2)


#[[
3 这个版本加入条件变量, 另外将 2 版本多余的注释和弃用的函数删除。

加入条件变量后, 确实没有了盲等现象。但是还有一个问题是如果一个定时器任务完成后, 如何在 jobArray[] 中将
这个定时器占的位置置为 nullptr , 并且释放掉内存?
这个版本也进行了这个问题的解决, 即再创建一个线程, 这个线程是 clear线程, 专门来释放已经执行完的定时器任务的内存以及腾出这个
执行完的定时器任务在jobArray[]中的位置。但是直接程序运行错误产生段错误。所以, 我注释调了这个线程。

这个 3 版本不足的地方已经在上面指出。4 版本进行继续改进这个问题。
]]
add_subdirectory(AnyTimer2_multithread3)


#[[
4 版本没有沿用 3 版本的思路, 因为我实现不出来。 我想到了一个新的思路就是, 直接在定时器的线程中退出的时候, 删除自己的内存, 并腾出
jobArray[] 的位置。

运行成功没有问题, 没有内存泄漏。
剩下最后一个就是 模块加载 那里在多线程中可能会 造成竞争, 所以下一个版本就是使用 pthread_once();函数来重构 moduleLoad();函数。
]]
add_subdirectory(AnyTimer2_multithread4)

#[[
使用 pthread_once(); 重构完成, 至此多线程安全的定时器设计完成。
]]
add_subdirectory(AnyTimer2_multithread5)

# 删除不必要的注释。
add_subdirectory(AnyTimer2_multithread6)


add_subdirectory(thread_test)




























